编程 零服务器代码智能引擎 GitNexus 深度解析:从知识图谱构建到 AI Agent 架构感知

2026-04-24 17:34:46 +0800 CST views 6

零服务器代码智能引擎 GitNexus 深度解析:从知识图谱构建到 AI Agent 架构感知

2026 年的 AI 编程工具,正在经历一场从"问答助手"到"架构感知智能体"的根本性转变。GitNexus 以其独特的零服务器架构和预计算关系智能,在 30 天内斩获近 5 万 Star,登顶 GitHub Trending 榜首。它解决的不是"怎么写代码"的问题,而是"AI 到底看没看懂你的代码库"这个更深层的问题。

本文将从架构设计、索引管道、图查询协议、Agent 集成实战四个维度,对 GitNexus 进行系统性深度剖析,并给出性能优化和落地建议。


一、背景:AI Agent 为什么会"盲写"代码

在真实项目开发中,AI 编程工具面临一个极其尴尬的局面:模型很强,但视野很窄

举几个常见场景:

  • 遗漏依赖:你想重构一个函数,AI 直接改掉,结果线上爆了——因为它没看到有 8 个地方在调用它。
  • 误解架构:AI 以为某段代码是核心逻辑,其实是废弃代码;以为某接口没人用,结果一删引发连环故障。
  • 上下文爆炸:为了避免遗漏,你不得不往 prompt 里塞大量文件内容,Token 飞涨,响应变慢,质量反而下降。

问题的根源在于:传统 RAG(Retrieval-Augmented Generation)只能搜索文本相似性,无法理解代码之间的结构关系。一段代码引用了哪些函数、属于哪个模块、被哪些调用链覆盖——这些信息对人类程序员来说是"代码感"的核心,但 AI 工具基本上是盲的。

GitNexus 的出现,就是要解决这个问题。它的核心命题是:把代码库的结构知识提前算好,存成一张可查询的图,然后让 AI Agent 随时来查。


二、核心架构:零服务器 + 双模式设计

2.1 设计哲学:不碰代码,只建图

GitNexus 的设计哲学很明确:代码永远留在本地,工具只负责理解它的结构,然后把理解结果存入本地数据库。整个过程没有任何数据离开用户的机器或浏览器。

这与很多云端代码分析工具形成了鲜明对比。云端工具通常要求你上传代码仓库、授予访问权限,而 GitNexus 直接在本地完成从解析到建图的全流程。

架构上,GitNexus 是一个 monorepo,分为三个主要包:

包名职责
gitnexus/CLI + MCP 服务端,索引管道,KuzuDB 图数据库,嵌入生成
gitnexus-web/Vite + React 浏览器端,交互式图谱浏览器 + AI 聊天
gitnexus-shared/TypeScript 类型定义和常量,供 CLI 和 Web 共享

这种拆分带来了一个非常优雅的效果:CLI 索引的仓库,可以无缝桥接到 Web UI 浏览器——你在终端跑完 gitnexus analyze,直接打开 gitnexus.vercel.app,就能看到同一份图谱,不需要重新上传或重新解析。

2.2 CLI + MCP 模式:日常开发主力

CLI + MCP 模式是 GitNexus 的核心使用路径。它的典型工作流是:

npx gitnexus analyze    # 索引代码库
npx gitnexus setup      # 配置 MCP(自动探测编辑器)
# → AI 编辑器(Cursur/Claude Code/Codex)通过 MCP 连接

索引完成后,GitNexus 启动一个 MCP 服务端(stdio 模式),等待来自 AI 编辑器的请求。编辑器通过 MCP 协议调用 GitNexus 提供的工具,工具返回结构化的图谱查询结果——不是文本片段,是完整的关系数据。

CLI 模式的核心优势:

  • 完整仓库支持:任意大小,不受浏览器内存限制
  • KuzuDB 原生绑定:持久化存储,索引结果不丢失
  • Tree-sitter 原生绑定:比 WASM 版本性能更高
  • 完全离线:没有任何网络请求,代码永不离开本地

2.3 Web UI 模式:快速探索和演示

Web UI 模式面向不需要安装的场景:把 GitHub 仓库 URL 或 ZIP 文件拖进去,立刻得到一个交互式知识图谱。

# 无需安装,直接访问
https://gitnexus.vercel.app

Web 模式使用 KuzuDB 的 WASM 版本,所有数据存储在浏览器内存中(每个会话独立),不需要服务端。适合快速演示、一次性代码分析、或在不方便安装 CLI 的环境里使用。

两种模式的核心差异:

维度CLI + MCPWeb UI
仓库规模完整仓库,无限制受浏览器内存限制(~5k 文件)
存储KuzuDB 原生(持久化)KuzuDB WASM(内存中)
解析引擎Tree-sitter 原生绑定Tree-sitter WASM
隐私完全本地,无网络完全在浏览器,无服务器
适用场景日常开发 + Agent 集成快速探索、演示

2.4 Bridge 模式:CLI + Web 的无缝连接

Bridge 模式是 GitNexus 最有意思的彩蛋功能:

gitnexus serve

运行后,本地 MCP 服务器启动 HTTP 桥接,Web UI 会自动发现本地索引过的所有仓库,然后可以直接浏览它们的图谱——无需重新上传,无需重新解析。

这意味着你可以在终端里完成深度索引,然后在浏览器里做可视化探索,两者共用同一份数据。


三、12 阶段索引管道:知识图谱是如何建成的

GitNexus 的索引管道是整个项目最核心的部分。它定义了如何将一堆源代码文件,转化为一组有语义的节点和边。

3.1 整体流程:12 个阶段的 DAG

索引管道由 12 个有序阶段组成,每个阶段有明确的依赖关系和类型化输出。整个管道从 analyze.ts 开始,调用 runFullAnalysis,最终产出存储在 KuzuDB 中的知识图谱。

scan → structure → [markdown, cobol] → parse → [routes, tools, orm]
 → crossFile → mro → communities → processes

这 12 个阶段构成了一个有向无环图(DAG),部分阶段可以并行执行。下面逐一解析:

Phase 1: scan(文件扫描)

这是管道的入口,遍历整个代码仓库,收集所有可解析的文件路径和大小信息。

// 伪代码示例:scan 阶段核心逻辑
function scanRepository(rootPath: string): FileEntry[] {
  const entries = walkRepository(rootPath);
  return entries
    .filter(isSourceFile)        // 过滤可解析的源文件
    .map(e => ({ path: e.path, size: e.size }));
}

输出:所有源文件路径 + 文件大小信息,供后续阶段使用。

Phase 2: structure(结构解析)

根据文件系统结构,建立 FileFolder 节点,以及它们之间的 CONTAINS 边。

Folder("src")
  └─ CONTAINS → File("service/user.ts")
  └─ CONTAINS → Folder("components")
       └─ CONTAINS → File("Button.tsx")

同时构建 allPathSet,用于快速判断某个路径是否存在,避免后续阶段重复查找。

Phase 3: markdown(Markdown 解析)

专门处理 .md.mdx 文件,提取文档结构(标题、章节、代码块),建立文档节点和跨文件链接边。这对于理解代码库的架构文档和设计意图非常重要。

Phase 4: cobol(COBOL 解析)

用正则表达式处理 COBOL 代码(树-sitter 不支持),提取程序、段落、节的层级结构。这是一个典型的差异化处理例子:不同语言类型使用不同解析策略。

Phase 5: parse(核心解析阶段)

这是最重量级的一个阶段,占据整个管道 15%~70% 的进度。使用 Tree-sitter 并行解析所有源文件,提取:

  • 符号节点FunctionClassMethodInterfaceVariable
  • 关系边IMPORTS(导入关系)、CALLS(调用关系)、EXTENDS(继承关系)

Tree-sitter 是 GitNexus 解析能力的基石。它是一个增量解析框架,使用语言特定的语法树查询(tree-sitter-queries.ts),能够:

  • 精确识别每种语言的语法结构
  • 增量解析(文件修改时只重解析受影响的部分)
  • 支持 Worker 池并行处理,充分利用多核 CPU
// parse 阶段使用 Worker 池并行处理
const workers = new WorkerPool(parseWorkerScript, {
  concurrency: os.cpus().length  // 自动利用本机所有 CPU 核心
});

for (const file of sourceFiles) {
  workers.submit({ filePath: file.path, content: file.content });
}

如果 Worker 池失败(如 Web Worker 环境不支持),自动降级为顺序处理,保证兼容性。

Phase 6: routes(路由解析)

parse 阶段的基础上,识别 Web 框架的路由定义,建立 Route 节点和 HANDLES_ROUTE 边。

支持的框架包括:

  • Next.js:文件系统路由和 app/ 目录
  • React Router:声明式路由定义
  • PHP:传统 MVC 路由
  • 装饰器路由:Express/Koa 等框架的装饰器语法
// 例如 Next.js 的 app/users/[id]/page.tsx
// 解析后建立:
Route("/users/[id]") ──HANDLES_ROUTE──→ File("app/users/[id]/page.tsx")

Phase 7: tools(MCP/RPC 工具解析)

识别项目中的 MCP 工具定义和 RPC 处理器,建立 Tool 节点和 HANDLES_TOOL 边。这对于理解 AI Agent 工具生态系统特别有价值——你能看到项目中哪些函数被暴露为可调用的工具。

Phase 8: orm(数据库查询解析)

分析 ORM 查询语句,建立 QUERIES 边,关联数据模型和它们的数据库操作。

支持的 ORM:

  • Prisma:查询构造器语法
  • Supabase:客户端 API 调用
// Prisma 查询
const users = await prisma.user.findMany({ where: { active: true } });
// 解析后建立:
File("service/user.ts") ─QUERIES──→ Model("User")

Phase 9: crossFile(跨文件分析)

这是整个管道中最关键的阶段之一。它按照拓扑导入顺序处理跨文件类型传播,解决:

  • 接口定义的跨文件实现
  • 类型别名和泛型的跨文件传递
  • 函数签名的跨文件一致性问题

cross-file 阶段的核心挑战在于循环依赖处理:如果 A 文件导入 B,B 又导入 A(常见于 React 组件和样式文件),解析顺序就变得复杂。GitNexus 使用拓扑排序算法处理这个问题,同时维护一个"待处理符号"队列来解决循环依赖。

Phase 10: mro(方法解析顺序)

处理方法覆盖和方法实现关系,建立 METHOD_OVERRIDESMETHOD_IMPLEMENTS 边。

// 子类覆盖父类方法
class Dog extends Animal {
  speak() { ... }  // METHOD_OVERRIDES → Animal.speak
}

MRO 阶段还处理菱形继承等复杂场景(Python 多继承),确保方法解析顺序的正确性。

Phase 11: communities(社区发现)

使用 Leiden 算法对代码库进行功能聚类,发现代码社区(Community)。

Leiden 算法是 Louvain 算法的改进版本,在保持社区划分质量的同时保证了算法永远收敛(不会陷入局部最优)。社区划分的依据是节点之间的连接密度——高度互联的函数/类会被分到同一个社区。

Community("支付模块")
  ├─ Member: PaymentService
  ├─ Member: PaymentGateway
  ├─ Member: OrderProcessor
  └─ Member: RefundHandler

Community("认证模块")
  ├─ Member: AuthService
  ├─ Member: TokenManager
  └─ Member: SessionStore

这个社区结构是 GitNexus 预计算关系智能的核心体现:当你查询一个函数时,工具不仅返回直接调用者,还能告诉你它属于哪个社区、这个社区有哪些相关模块。

Phase 12: processes(进程/执行流分析)

最终阶段,建立 Process 节点和 STEP_IN_PROC 边,描述代码的执行流程。

Process 可以理解为"业务场景"或"用户故事"级别的抽象——一组相关的 API 路由、工具处理器和数据库操作,共同完成一个业务功能。

至此,12 个阶段全部完成,知识图谱构建完毕,数据存入 KuzuDB。


四、图数据库选型:为什么是 KuzuDB

4.1 图数据库对比

GitNexus 选择 KuzuDB(而非 Neo4j 或其他图数据库)有几个非常实际的原因:

特性KuzuDBNeo4j传统关系型
部署方式嵌入式(无服务端)需要独立服务通用
WASM 支持✅ 原生
性能(多跳查询)优秀优秀差(需要 JOIN)
许可证Apache 2.0GPL(商业版付费)通用
体积轻量中等通用
查询语言Cypher(兼容)CypherSQL

KuzuDB 是一个嵌入式图数据库,使用 Apache 2.0 许可证,可以直接编译进 Node.js 应用或 WASM 模块中。这使得 GitNexus 能同时支持:

  • CLI 模式:KuzuDB 原生库,性能最优
  • Web 模式:KuzuDB WASM,零服务端依赖

4.2 Cypher 查询接口

GitNexus 暴露了 Cypher 查询接口,允许用户直接写图查询语句:

-- 查找某函数的所有调用者(递归,向下)
MATCH (caller)-[:CALLS*]->(target {name: 'processPayment'})
RETURN caller.name, caller.file

-- 查找某模块的所有下游影响
MATCH (module)-[:CALLS*]->(affected)
WHERE module.name = 'OrderService'
RETURN affected.name, affected.type

这种直接查询能力,使得 GitNexus 不只是一个给 AI 用的工具,人类开发者也可以用它来做代码分析、影响评估和架构探索。


五、MCP 工具集:AI Agent 的架构之眼

GitNexus 通过 MCP 协议暴露了 10+ 个结构化工具,每个工具都返回预计算好的图谱数据,而不是原始文本片段。

5.1 核心工具一览

工具名用途
query混合搜索(BM25 + 向量),在图谱中检索
context获取某符号的完整上下文(调用者、被调用者、所在进程)
impactBlast Radius 分析(上游影响 + 下游影响 + 风险摘要)
cypher直接执行 Cypher 查询
detect_changes将 Git diff 映射到受影响的符号和进程
rename图辅助的多文件重命名(支持 dry_run 预览)
api_impactAPI 路由变更前的变更影响报告
route_mapAPI 路由 → 处理器 → 消费者映射
tool_mapMCP/RPC 工具定义和处理器映射
shape_check响应结构与消费者属性访问的不匹配检测

5.2 预计算关系智能:核心创新点

传统 Graph RAG 的工作方式是:给 LLM 提供图的边和节点,让 LLM 自己探索关系。但这种方案有两个问题:

  1. LLM 需要多轮查询:因为 LLM 不确定关系在哪里,需要逐步探索,每次查询都是一次 API 调用
  2. Token 浪费:每次探索都要把中间结果传回 LLM,上下文爆炸

GitNexus 的预计算关系智能,则是在索引阶段就把所有关系算好:

  • 影响范围(Impact):预计算每个函数的上游调用链和下游被调用者,工具一次调用返回完整结果
  • 社区归属:每个函数都知道自己属于哪个社区(Phase 11),LLM 无需猜测
  • 风险评分:影响分析结果包含置信度评估,告诉你这个分析的可信度有多高

举个例子,当你调用 impact 工具查询一个 API 路由:

{
  "tool": "api_impact",
  "symbol": "POST /api/orders",
  "return": {
    "callers": ["OrderController.create", "WebhookHandler.orderCreated"],
    "callees": ["OrderService.create", "PaymentGateway.charge"],
    "communities": ["订单模块", "支付模块"],
    "downstream_apis": ["/api/orders/:id", "/api/orders/:id/refund"],
    "confidence": 0.94,
    "risk_level": "high"
  }
}

一次调用,完整上下文,不需要任何额外探索

5.3 多仓库 MCP 架构

GitNexus 使用全局注册表(~/.gitnexus/registry.json),一个 MCP 服务器可以同时服务多个已索引的仓库。

// registry.json 结构
{
  "repos": [
    { "path": "/Users/me/project-a", "lastIndexed": "2026-04-24", "lastCommit": "abc123" },
    { "path": "/Users/me/project-b", "lastIndexed": "2026-04-20", "lastCommit": "def456" }
  ]
}

这意味着你不需要为每个项目单独配置 MCP——索引完成后,AI 编辑器可以随时切换仓库上下文。


六、Editor 集成:Claude Code + Cursor + Codex

6.1 Claude Code 深度集成

GitNexus 对 Claude Code 的支持最完整,包括三重增强:

  1. MCP 连接:Claude Code 通过 MCP 协议调用 GitNexus 工具
  2. Skills:预置的 .claude/skills/gitnexus/ 目录,包含工具使用指南
  3. HooksPreToolUse + PostToolUse 钩子,在工具调用前后自动注入上下文
// Claude Code hooks 配置(自动生成)
{
  "hooks": {
    "PreToolUse": [{ "name": "gitnexus-inject-context" }],
    "PostToolUse": [{ "name": "gitnexus-validate-impact" }]
  }
}

PreToolUse 钩子在每次工具调用前检查是否需要注入相关代码上下文;PostToolUse 钩子在工具返回后验证修改是否引入了潜在的破坏性变更。

6.2 Cursor + Codex 集成

Cursor 通过 MCP + Skills 模式集成,Codex 同理。这两者都支持 MCP 协议,因此可以无缝调用 GitNexus 的图谱工具,只是没有 Claude Code 的 Hooks 增强。

6.3 模型民主化效应

GitNexus 文档中有一个值得注意的观点:即使是小模型,配合 GitNexus 也能获得接近大模型的代码理解能力

原因在于:GitNexus 把"理解代码结构"这个认知密集型任务,从模型推理转移到了工具计算。小模型不需要自己探索调用链、推断影响范围——工具已经算好了,直接用就行。这是一种任务卸载的设计哲学。


七、实战:把 GitNexus 用在真实项目中

7.1 安装和索引

# 全局安装
npm install -g gitnexus

# 进入项目目录
cd ~/my-project

# 一键索引(整个流程:扫描 → 解析 → 建图 → 存储)
npx gitnexus analyze

# 配置编辑器 MCP
npx gitnexus setup

setup 命令会探测你安装的编辑器,自动写入全局 MCP 配置:

// ~/.config/mcp.json
{
  "mcpServers": {
    "gitnexus": {
      "command": "gitnexus",
      "args": ["mcp", "serve"],
      "env": {}
    }
  }
}

7.2 在 Claude Code 中使用

索引完成后,在 Claude Code 对话中直接使用:

用户:重构 handlePayment 函数,把错误处理逻辑抽出来
Claude Code:
  → 调用 gitnexus impact 查询 handlePayment 的影响范围
  → 收到完整调用链:3个调用者,2个下游依赖,社区"支付模块"
  → 确认所有调用者后,再进行重构
  → 重构完成后,调用 gitnexus detect_changes 验证影响

整个过程不需要用户手动告诉 Claude Code"有 3 个地方在调用这个函数"——工具自己就知道。

7.3 图可视化探索

# 启动本地服务
gitnexus serve

# 打开浏览器
# 访问 https://gitnexus.vercel.app 或本地地址
# 自动发现所有 CLI 索引过的仓库

Web UI 提供了:

  • 交互式图谱:缩放、拖拽、节点高亮
  • AI 聊天:基于当前图谱上下文提问
  • 路径追踪:选择两个节点,高亮最短路径

7.4 Blast Radius 分析实战

假设你要修改一个核心数据模型的字段:

# 使用 CLI 直接查询
gitnexus impact --symbol "User.email"

输出示例:

🔍 Blast Radius Analysis: User.email

📊 Summary
  Confidence: 96%
  Risk Level: HIGH
  Total Affected: 23 symbols across 8 files

📥 Upstream (who calls this):
  ├─ auth/login.ts: authenticateByEmail()
  ├─ auth/forgot.ts: sendResetLink()
  ├─ user/profile.ts: updateProfile()
  └─ 5 more...

📤 Downstream (what this calls):
  ├─ UserModel schema
  └─ validation.ts: validateEmail()

🏘️ Communities:
  ├─ 认证模块 (3 members)
  └─ 用户模块 (7 members)

⚠️ 建议修改前确认:
  - 所有 8 个文件的单元测试覆盖情况
  - 数据库迁移脚本是否需要调整
  - API 文档是否需要同步更新

八、性能优化与最佳实践

8.1 索引性能

GitNexus 的索引性能主要受以下因素影响:

优化维度具体做法
并行解析Worker 池自动利用所有 CPU 核心,无需手动配置
AST 缓存LRU 缓存已解析的 AST,避免重复解析
增量索引仅重解析自上次索引以来变更的文件
增量更新staleness.ts 检测 lastCommit vs HEAD,只更新有变更的区域

8.2 存储优化

  • CLI 模式:KuzuDB 原生存储在 .gitnexus/ 目录下,随 Git 忽略
  • 清理策略gitnexus clean 命令清理过期索引
  • 多仓库共享:同一台机器上索引过的所有仓库共享注册表

8.3 Token 效率

使用 GitNexus 后,Claude Code 等工具的 Token 消耗会显著下降,因为:

  1. 不需要在 prompt 中塞大量上下文:工具直接提供结构化数据
  2. 减少多轮探索:一次工具调用返回完整影响分析,无需 10+ 次查询链
  3. 小模型也能工作:降低对超大上下文窗口模型的依赖

8.4 常见问题排查

索引失败:检查 gitnexus debug analyze 输出,常见原因包括文件权限、语法树不支持的语言扩展。

MCP 连接失败:运行 npx gitnexus setup --force 重新配置,检查 MCP 配置文件路径是否正确。

内存占用过高:大仓库建议使用 CLI 模式(原生存储性能更好),Web 模式适合中小仓库。


九、技术局限与社区演进方向

9.1 当前局限

  1. 语言覆盖:虽然支持 11+ 种语言(TypeScript、Python、Java、Go、Rust 等),但每种语言的语法解析质量参差不齐,小众语言可能解析不完整
  2. 动态特性:无法分析运行时才确定的依赖(如反射、动态导入),这些在静态解析层面是盲区
  3. 增量索引精度:跨文件的增量更新有时需要完整重建(如某个文件的修改影响了大量跨文件类型传播)

9.2 未来方向

从 GitHub README 和企业版路线图来看,GitNexus 正在推进:

  • PR Review:自动化 Pull Request 影响范围分析(企业版已支持)
  • Auto regression forensics:自动追溯变更引入的回归问题
  • E2E test generation:基于图谱自动生成端到端测试用例
  • OCaml 支持:扩展语言覆盖
  • Self-hosted 部署:企业版的自托管选项

十、总结:代码知识图谱的意义

GitNexus 带来的变革,不只是"让 AI 编程工具更准"这么简单。它本质上在推动一个更大的范式转换:

从"把代码塞给 AI"到"让 AI 主动理解代码结构"

传统工作流中,程序员是代码和 AI 之间的翻译者——程序员自己理解代码结构,然后把相关部分提取出来喂给 AI。GitNexus 把这个翻译工作自动化了:AI 可以直接查询图谱,自己获取所需的结构上下文。

这意味着:

  • AI 的能力边界,不再受人类"上下文工程"能力的限制
  • 小团队也能维护大型代码库:新成员借助 GitNexus 的图谱,可以快速理解架构,降低学习成本
  • AI Agent 的可靠性提升:工具提供的预计算结果有明确的置信度,AI 可以据此判断自己是否理解了代码

GitNexus 的 Star 增长曲线(单日 +857 星)和企业版的快速推出,都在说明一件事:代码知识图谱这个方向,已经被市场验证。它不是一个小众工具,而是 AI 编程基础设施的重要组成部分。

如果你正在构建 AI Agent 应用,或者想让现有的 AI 编程工具从"能写代码"进化到"真正看懂代码",GitNexus 值得深入研究和集成。


参考资源

  • GitNexus GitHub:https://github.com/abhigyanpatwari/GitNexus
  • 在线体验:https://gitnexus.vercel.app
  • 架构文档:https://github.com/abhigyanpatwari/GitNexus/blob/main/ARCHITECTURE.md
  • 企业版:https://akonlabs.com

推荐文章

php指定版本安装php扩展
2024-11-19 04:10:55 +0800 CST
WebSQL数据库:HTML5的非标准伴侣
2024-11-18 22:44:20 +0800 CST
一些好玩且实用的开源AI工具
2024-11-19 09:31:57 +0800 CST
# 解决 MySQL 经常断开重连的问题
2024-11-19 04:50:20 +0800 CST
html夫妻约定
2024-11-19 01:24:21 +0800 CST
Vue3中的v-bind指令有什么新特性?
2024-11-18 14:58:47 +0800 CST
Vue3中如何处理WebSocket通信?
2024-11-19 09:50:58 +0800 CST
css模拟了MacBook的外观
2024-11-18 14:07:40 +0800 CST
php 统一接受回调的方案
2024-11-19 03:21:07 +0800 CST
CSS 特效与资源推荐
2024-11-19 00:43:31 +0800 CST
Golang中国地址生成扩展包
2024-11-19 06:01:16 +0800 CST
Go语言SQL操作实战
2024-11-18 19:30:51 +0800 CST
#免密码登录服务器
2024-11-19 04:29:52 +0800 CST
Golang在整洁架构中优雅使用事务
2024-11-18 19:26:04 +0800 CST
程序员茄子在线接单