跳到主要内容

nao 是什么 · 全景与阅读地图

30 秒导读: nao 是一个开源的"分析 agent"框架。数据团队先用一个 Python 命令行工具(nao-core)把公司的数据库、表结构、文档、规则编成一个"上下文文件夹";然后启动一个聊天网页,业务同事用自然语言问数("上季度北美营收环比多少?"),后台的 AI agent 会像人一样在这个文件夹里翻找、对真实数仓执行 SQL、把结果画成图表或数据故事,一边生成一边流式显示在聊天里。本章只给"大盘",各机制细节留给 01–05 章。


1. 这是什么(零基础也能懂)

一句话定义: nao 是一个面向数据团队的开源分析 agent 框架——GitHub 仓库 getnao/nao,pip 包名 nao-core(见 cli/pyproject.toml:2)。

它服务两类完全不同的人,这一点是理解全局的钥匙:

读者他们用 nao 干什么交互方式
数据团队 / 分析工程师搭建并"喂养"agent 的上下文:接数仓、灌元数据、写规则、跑评测nao-core 命令行(init / sync / chat / test)
业务用户用大白话问数、看图、给反馈浏览器里的聊天 UI(http://localhost:5005)

为什么需要它: 让 LLM 回答"我们公司的数据问题"难点从来不是"调用模型",而是模型不知道你的表长什么样、指标怎么算、口径有哪些坑。nao 的答案是:把这些知识沉淀成文件,让 agent 能按需翻阅,而不是一次性塞进提示词。

核心能力(README.md:36-50):

  • 🧱 开放的上下文构建器 —— 数据、元数据、建模、文档、工具、MCP,想放什么放什么,像文件系统一样组织。
  • 🏳️ 数据栈无关 —— 任意数仓、任意 LLM 都能接(cli/pyproject.toml:48-69 列了 Postgres/BigQuery/Snowflake/DuckDB… 和 OpenAI/Anthropic/Mistral/Gemini/Ollama)。
  • 🔒 可自托管、用自己的 key —— 数据不出你的环境。
  • 🤖 自然语言到洞察 + 原生可视化 + 透明推理 + 一键反馈 —— 面向业务用户的四件套。

用起来什么样(README.md:52-113): 数据团队跑五条命令就有了一个能问数的 agent。

pip install nao-core # 1. 装 CLI
nao init # 2. 建项目(问你项目名、要不要连库/仓库/LLM key/Slack)
nao debug # 3. 自检配置
nao sync # 4. 把数据源同步进"上下文文件夹"
nao chat # 5. 启动聊天 UI,浏览器打开 localhost:5005 开始问数

一句话直觉/类比: 把 nao 想成给 AI 配了一个"公司数据资料室 + 一部能连数仓的电话"nao-core 负责把资料室建好、上架资料;聊天里的 agent 负责进资料室翻资料、必要时打电话(SQL)去仓库取数,再把答案讲给你听。

本节不出现底层代码;下面开始看"大盘怎么转"。


2. 顶层全景(它大概怎么转)

2.1 两半架构:CLI 建上下文,应用跑对话

nao 的代码天然分成两半,分工干净:

半边是什么语言/技术职责一句话
cli/(nao-core)上下文构建器 + 启动器Python(cyclopts、ibis、pydantic)init 搭骨架、sync 灌数据、chat 启动应用
apps/(nao-chat)agent 运行时 + 聊天 UITypeScript monorepo(Fastify、React)跑 agent 主循环、渲染流式对话
FastAPI 边车SQL 执行进程Python(FastAPI,在 cli/ 侧)专门对真实数仓执行 SQL 并回表格

这里有个关键设计:执行 SQL 的活留在 Python 侧(因为数仓驱动、ibis 生态都在 Python),而 agent 的"大脑"在 TypeScript 侧(用 Vercel AI SDK)。两者通过一个本地 HTTP 边车连起来——TS 里的 SQL 工具就是去 http://localhost:${FASTAPI_PORT}/execute_sql 发一个 POST(apps/backend/src/agents/tools/execute-sql.ts:25)。

nao chat 到底启动了什么? CLI 并不"实现"聊天;它把预先编译好的服务器二进制 nao-chat-server 跑起来(cli/nao_core/commands/chat.py:44 get_server_binary_path),再拉起 FastAPI 边车(chat.py:59 get_fastapi_main_path),默认端口 5005(chat.py:21)。也就是说 apps/ 这半边被打包进了 CLI 一起分发。

2.2 monorepo 布局(apps/ 三个 workspace + cli/)

package.json:4 声明 workspaces: ["apps/*"],三个 TS 包加一个 Python 包:

目录包名干什么关键栈
apps/backend/@nao/backendAPI 服务器 + agent 主循环 + tRPC 路由 + DB。内含 fastapi/ SQL 边车Fastify、tRPC、Drizzle ORM、Vercel AI SDK(ai)、better-auth
apps/frontend/@nao/frontend聊天 UI:流式渲染、图表、Monaco/表格编辑React 19、TanStack Router/Query/Table、Shadcn、Recharts、streamdown
apps/shared/@nao/shared前后端共享的工具/类型(体量很小)TS
cli/nao-core(pip)上下文构建器 CLI + FastAPI 边车cyclopts、ibis-framework、pydantic、jinja2、apscheduler

(布局取材:CLAUDE.md:9-14、各 package.jsoncli/pyproject.toml。)

后端把能力按角色分层:routes/(HTTP 入口)、handlers/(编排)、services/(业务,agent 主循环在 services/agent.ts)、agents/tools/(工具带)、queries/(DB 访问)、db/(Drizzle schema,同时支持 SQLite 与 Postgres)。

2.3 一张顶层数据流图

怎么读这张图: 从上到下是一次问数的生命周期。左半虚线框是运行期(用户问一句触发),右边灰块是准备期(数据团队事先用 CLI 搭好)。agent 的两只"手":①用文件系统工具翻上下文文件夹,②经FastAPI 边车对真实数仓跑 SQL。

┌───────────────────────── 准备期(数据团队,离线) ─────────────────────────┐
│ nao init → nao sync 把 数据库/表结构/文档/规则 编成 ↓ │
│ 「上下文文件夹」(项目 projectFolder) │
└───────────────────────────────────┬──────────────────────────────────────┘
│ 被 agent 当成可浏览的文件系统
── 运行期(业务用户在浏览器) ─────────┼──────────────────────────────────────

① 用户提问 ──────────► 前端聊天 UI (@ai-sdk/react useChat)
"上季度营收环比?" │ POST /agent { chatId, message }

后端 routes/agent.ts ──► handlers/agent.ts
│ agentService.create(...)

┌─────────── Agent 主循环 (ToolLoopAgent) ───────────┐
│ 想 → 调工具 → 看结果 → 再想 …(最多 ~20 步) │
│ │
│ ├─ read / list / grep / search ──► 翻「上下文文件夹」
│ │ (找到相关表/口径/规则)
│ │ │
│ └─ execute_sql ──HTTP──► FastAPI 边车 /execute_sql ──► 真实数仓
│ (返回结果表) │
│ │
│ 最后:display_chart / story ──► 生成图表 / 数据故事 │
└──────────────────────────┬──────────────────────────┘
│ toUIMessageStream(逐块)

前端流式渲染:文字 + 图表 + 表格 + 引用来源

② 用户看到答案、可点赞/点踩反馈 ◄──────┘

2.4 主线走一遍(高层,不进代码)

  1. 准备期。 数据团队跑 nao init(cli/nao_core/commands/init.py)生成项目骨架 + nao_config.yaml + RULES.md;再跑 nao sync(cli/nao_core/commands/sync/)把数据库表结构、仓库、文档等物化成上下文文件夹里的文件。→ 细节见 01-context-filesystem.md

  2. 启动。 nao chat 拉起 nao-chat-server(即 apps/backend,入口 apps/backend/src/index.ts:3 startServer({ port: 5005 }))和 FastAPI 边车。后端用 Fastify 挂上 tRPC 与 agent 路由(apps/backend/src/app.ts:148 注册 agentRoutes)。

  3. 提问。 业务用户在前端输入问题;@ai-sdk/reactuseChat 把消息 POST 到后端 /agent(apps/backend/src/routes/agent.ts:16)。路由做鉴权(viewer 不能发消息),交给 handleAgentRoute(apps/backend/src/handlers/agent.ts:26)。

  4. 主循环。 handler 用 agentService.create(...)(apps/backend/src/handlers/agent.ts:62)造出一个 ToolLoopAgent(apps/backend/src/services/agent.ts:323,底层是 Vercel AI SDK 的 ai 包)。agent 反复"思考→调工具→读结果",直到给出答案(stepCountIs(20) 兜底,services/agent.ts:214)。→ 细节见 02-agent-loop.md

  5. 两只手。 agent 用 read/list/grep/searchprojectFolder 里导航上下文(如 apps/backend/src/agents/tools/read.ts:13context.projectFolder);用 execute_sql 经边车对真实数仓取数;用 display_chart/story 产出可视化。→ 工具带细节见 03-tools.md

  6. 流式回传。 结果通过 toUIMessageStream(services/agent.ts:431)逐块推给前端,文字/图表/表格/来源一边生成一边显示。系统提示本身是用 .tsx 组件拼出来再渲染成 Markdown 的(apps/backend/src/components/ai/)→ 见 04-system-prompt.md

  7. 可靠性与部署。 上线前可用 nao test 做评测(README.md:114-127);生产可用 Docker 自托管、上下文可从 git 拉取、可定时刷新、还能接 Slack/Teams/Telegram/WhatsApp。→ 见 05-reliability-deploy.md


3. 阅读地图(建议顺序)

本章是 Layer 0(这是什么)+ Layer 1(顶层全景)。想深入某个机制,按下面顺序下钻——每章只讲一件事,互不重复:

顺序章节一句话讲什么什么时候读
101-context-filesystem.md上下文即文件系统:nao-core 怎么把数据源 sync 成一个 agent 能用文件工具浏览的上下文文件夹想搞懂"agent 的知识从哪来"
202-agent-loop.mdAgent 主循环:ToolLoopAgent 如何多步思考、调工具、把结果流回前端想搞懂"agent 怎么思考/执行"
303-tools.md工具带:SQL(经边车)、图表、数据故事、沙箱执行的边界想搞懂"agent 手里有哪些动作"
404-system-prompt.md系统提示与上下文装配:用 .tsx 组件拼提示词、注入用户规则与表结构(JSX→Markdown)想搞懂"提示词怎么组装"
505-reliability-deploy.md可靠性、自动化与自托管:评测框架、定时任务、Docker/git 上下文、多渠道接入想把它跑到生产

4. 边界与本章不讲的

  • 本章只给大盘:各机制的真实实现(sync 的 provider 体系、主循环的 prepareStep/停止条件、SQL 边车的类型转换、提示词的 JSX 组装)都留给对应章节,这里不深入。
  • 代码走读(密集 file:line)不在总览页;去各章看。
  • nao 是框架而非托管服务:它给你构建器和运行时,数据、LLM key、部署都在你自己手里(README.md:43 "Self-hosted & secure")。

5. 全局代码地图(导航索引)

这是一张跨全项目的顶层跳转表——想直接进源码时用符号名 grep(比行号抗漂移)。各章还有更细的本地代码地图。

主题文件关键符号
CLI 入口(注册所有命令)cli/nao_core/main.py:22Appapp.command(...)main
包定义(名字/依赖/数仓·LLM 可选组)cli/pyproject.toml:2name = "nao-core"[project.scripts] nao
nao chat 启动服务器 + 边车cli/nao_core/commands/chat.py:44get_server_binary_pathget_fastapi_main_path
上下文同步(灌数据源)cli/nao_core/commands/sync/sync 命令、providers/
本地/git 上下文源cli/nao_core/context/local.pygit.pybase.py
FastAPI SQL 边车apps/backend/fastapi/main.py:222execute_sqlhealth_checkrefresh_context
后端启动apps/backend/src/index.ts:3startServer
Fastify 装配(挂 tRPC + agent 路由)apps/backend/src/app.ts:148agentRoutesfastifyTRPCPlugin
Agent HTTP 路由(问数入口)apps/backend/src/routes/agent.ts:16agentRoutesapp.post('/')
请求编排apps/backend/src/handlers/agent.ts:26handleAgentRoute
Agent 主循环apps/backend/src/services/agent.ts:159AgentServiceToolLoopAgent(:323)、agentService(:909)
工具带(注册表)apps/backend/src/agents/tools/index.ts:19toolsgetTools
SQL 工具(调边车)apps/backend/src/agents/tools/execute-sql.ts:25fetch(.../execute_sql)
上下文导航工具apps/backend/src/agents/tools/readlistgrepsearch
可视化工具apps/backend/src/agents/tools/display-chartstory
系统提示装配apps/backend/src/agents/system-prompts.ts:27getSystemPromptOverrideinjectNaoPrompt
提示词 JSX 组件apps/backend/src/components/ai/memory-system-prompt.tsx.tsx
LLM Provider 工厂apps/backend/src/agents/providers.ts:1LLM_PROVIDERScreate*(OpenAI/Anthropic/…)
DB schema(SQLite + Postgres 双写)apps/backend/src/db/sqlite-schema.tspg-schema.ts
前端聊天(流式)apps/frontend/src/queries/use-chat-query.ts:5useChatQuery(基于 @ai-sdk/react useChat)
部署/自托管配置docker-compose.ymlREADME-dockerhub.mdNAO_CONTEXT_SOURCENAO_DEFAULT_PROJECT_PATH

说明:ToolLoopAgentstreamText 家族均来自 Vercel AI SDK 的 ai 包(apps/backend/src/services/agent.ts:10-19 的 import),不是 nao 自造的循环——nao 在其上加了工具带、停止条件与上下文装配。