首页
/ Onyx(原 Danswer)仓库 AI 智能体开发指南:环境搭建、测试体系与工程规范全解析

Onyx(原 Danswer)仓库 AI 智能体开发指南:环境搭建、测试体系与工程规范全解析

2026-09-09 09:12:17作者:庞队千Virginia

Onyx(曾用名 Danswer)是开源的 Gen-AI 与企业级搜索平台,将企业文档、应用与人连接,并支持连接任意 LLM 的智能对话。本文以仓库根目录的 CLAUDE.md 项目知识库为主线,完整讲解在该仓库中进行 AI 智能体(Agent)开发所需的全部工作流——从 uv 虚拟环境、测试密钥解析、四层测试体系,到 Celery 后台任务、错误处理、代码质量与写作规范,并结合源码逐项实证,帮助开发者快速上手并为仓库做出高质量贡献。

仓库定位与技术栈总览

CLAUDE.md 明确指出:Onyx(前身 Danswer)是一个开源 Gen-AI 与企业搜索平台,采用模块化架构,同时提供 MIT 许可的 Community Edition(社区版,即 backend/onyx/ 核心)与企业版(Enterprise Edition,即 backend/ee/onyx/)两种形态。

其技术栈全景如下:

层次 技术选型
后端 Python 3.13、FastAPI、SQLAlchemy、Alembic、Celery
前端 Next.js 16、React 19、TypeScript、Tailwind CSS
数据库 PostgreSQL(关系库)+ Redis(缓存)
搜索 OpenSearch 支撑的关键词与向量文档索引
认证 OAuth2、SAML,多提供商支持
AI/ML LangChain、LiteLLM、多种 embedding 模型

值得注意:虽然旧版本及部分迁移脚本中仍残留 "Vespa" 字样,但根据 backend/AGENTS.md 的说明,OpenSearch 才是当前文档索引后端,Vespa 相关内容仅是兼容性或迁移遗留物(除非活跃的 DocumentIndex 工厂/配置路径显式使用它们)。

开发环境准备:KEY NOTES 逐条详解

CLAUDE.md 的 Key Notes 部分定义了在该仓库工作的第一性原则,下面逐条展开并结合源码给出可执行命令。

1. Python 依赖与 uv 虚拟环境

Python 依赖全部托管在仓库根目录的 uv 管理的虚拟环境 .venv 中。如果 .venv 尚不存在,用以下命令创建并激活:

uv sync --frozen
source .venv/bin/activate

--frozen 表示严格按锁文件(uv.lock)安装,不更新依赖版本。根据 CONTRIBUTING.md 的补充,推荐显式指定 Python 3.13 创建环境:

uv venv .venv --python 3.13

版本前提:Python 3.13 是仓库基准版本;3.14 暂不支持(部分依赖如 onnxruntime 与 CUDA 版 torch 尚未发布 3.14 wheel),其他版本可能需要代码或依赖改动。Windows 下激活命令为 .venv\Scripts\activate(PowerShell 用 Activate.ps1)。

运行 pytest 时可直接从仓库根目录通过 uv run 执行,无需手动激活环境——uv run 会自动使用锁文件锁定的环境,并在需要时创建/同步 .venv

2. 测试密钥(Test Secrets)解析链

测试中用到的 API Key 等密钥由 backend/tests/utils/aws_secrets.py 统一解析,解析顺序严格为:

  1. 进程环境变量(已设置的环境变量优先);
  2. .vscode/.env(被 gitignore,同时被 backend/AGENTS.md 中的 pytest 命令使用;创建方式见下文);
  3. AWS Secrets Manager(需先执行 aws sso login)。

源码实现印证:aws_secrets.py 中的 _get_local_secrets() 先查 os.environ,再通过 dotenv_values 读取 .vscode/.env(路径由 _DOTENV_PATH 拼接得出);剩余未解析的密钥通过 _get_aws_secrets() 调用 batch_get_secret_value 批量拉取,单批最多 20 个密钥 ID(_AWS_BATCH_GET_MAX_IDS = 20)。

  • AWS 区域通过环境变量 AWS_REGION 配置,默认 us-east-2
  • 密钥名按枚举类型推导 AWS 前缀:TestSecret 对应 test/DeploySecret 对应 deploy/(见 secret_names.py);
  • 混合不同环境的枚举会在类型检查阶段被拒绝(get_secrets 的重载与运行时校验双保险)。

测试声明机制:测试通过 @pytest.mark.secrets(TestSecret.X) 声明所需密钥。pytest 插件 pytest_secrets.pypytest_collection_modifyitems 阶段收集全部测试声明的密钥并集,然后由 session 级 fixture test_secrets 一次性批量解析;返回的 RedactedDict 重写了 __repr__<redacted>,防止密钥泄漏到测试输出中。

TestSecret 枚举覆盖范围很广:从 OPENAI_API_KEYANTHROPIC_API_KEYCOHERE_API_KEYAZURE_API_KEYLITELLM_API_KEY 等 LLM 提供商密钥,到 Confluence、Jira、Google Drive、Slack、Notion、SharePoint、Box 等数十个连接器的测试凭据,均可通过该机制声明。

关键约定:如果需要的密钥始终无法解析,应当询问用户而不是跳过测试——这是仓库对 Agent 的明确要求。

创建 .vscode/.env:复制 .vscode/env_template.txt.vscode/.env 并填写 <REPLACE THIS> 占位值。模板中的核心变量包括:

变量 说明 示例/默认
AUTH_TYPE 认证类型 basic
USER_AUTH_SECRET 签名密码重置、邮箱验证、OAuth 登录态与验证码 Cookie;生产必填 openssl rand -hex 32 生成
DEV_MODE 开发模式 true
LOG_ONYX_MODEL_INTERACTIONS 将模型 prompts、推理与回答打印到 stdout False
LOG_LEVEL 日志级别 debug
GEN_AI_API_KEY / OPENAI_API_KEY LLM 密钥,避免每次在 UI 手动配置 必填
GEN_AI_MODEL_VERSION 默认模型 gpt-4o
ENABLE_PAID_ENTERPRISE_EDITION_FEATURES 企业版开关 无付费许可证必须为 False
S3_ENDPOINT_URL MinIO 文件存储配置 http://localhost:9004minioadmin
OPENSEARCH_INITIAL_ADMIN_PASSWORD OpenSearch 初始管理员密码 本地开发任意值即可

3. Playwright 前端探索账号

使用 Playwright 探索前端时,登录凭据固定为:

  • 用户名:admin_user@example.com
  • 密码:TestPassword123!

该账号由 Playwright global setup 自动创建(见 constants.ts 中的 TEST_ADMIN_CREDENTIALS)。如果账号尚不存在,可通过注册页注册——第一个注册的用户自动成为 admin。应用访问地址为 http://localhost:3000

constants.ts 中还定义了 TEST_ADMIN2_CREDENTIALSWORKER_USER_POOL_SIZE = 8:并行 worker 用户池,供多 worker 并发场景使用 workerIndex % WORKER_USER_POOL_SIZE 取模分配(重试会生成递增的 workerIndex,取模可避免越界)。

4. 服务与日志状态确认

开发时默认假设所有 Onyx 服务都在运行。验证方式是检查 backend/log 目录下是否持续产出对应服务的日志——所有 Onyx 服务(api_server、web_server、celery_X)都会把日志 tail 到该目录。

5. 连接 Postgres 数据库

主机 checkout 与 devcontainer 内均可使用:

PGPASSWORD="${POSTGRES_PASSWORD:-password}" psql -h "${POSTGRES_HOST:-localhost}" -U postgres -c "<SQL>"

若本机没有 psql 客户端,回退到 Docker 容器内执行(注意不加 -it,因为 Agent shell 没有 TTY):

docker exec onyx-relational_db-1 psql -U postgres -c "<SQL>"

6. 后端调用必须走前端

调用后端 API 时始终经由前端代理,例如访问 http://localhost:3000/api/persona 而不是 http://localhost:8080/api/persona。这一约定保证了认证 Cookie、CSRF 与 Next.js 代理层的统一处理(前端读取与后端一致的 AUTH_COOKIE_NAME,见 constants.pyFASTAPI_USERS_AUTH_COOKIE_NAME 的定义)。

仓库布局与子项目规范

每个子项目都有独立的 agents 规范文件,进入相应目录前必须先阅读:

  • backend/AGENTS.md — FastAPI 应用 + Celery workers。onyx/ 是社区版核心,ee/ 镜像其目录结构承载企业功能,alembic/ 存放数据库迁移,tests/ 是测试套件。规范覆盖 Celery、迁移、测试、错误处理、LLM 追踪。
  • web/AGENTS.md — Next.js 前端,其规范同样覆盖 desktop/(Tauri shell)。
  • mobile/AGENTS.md — React Native + Expo 应用。移动端与 Web 差异显著(无 DOM、NativeWind、expo-router),不要想当然套用 Web 规则。

CLAUDE.md 还建议:探索目录树用 ls 而非依赖文档来获取完整包列表,避免文档与代码库脱节。

Celery 后台任务体系(源码级)

backend/AGENTS.mdcelery/apps/ 目录可看到完整的 Celery worker 体系:

Worker 职责
primary 协调核心后台任务:连接器管理/删除、文档索引同步、剪枝检查、LLM 模型更新、用户文件同步
docfetching 从连接器拉取文档,派生 docprocessing 任务;对卡死连接器做 watchdog
docprocessing 索引流水线:upsert 文档到 Postgres、分块、经 model server 嵌入、写块到文档索引、更新元数据
light 快速轻量操作:元数据同步、权限 upsert、检查点/索引尝试清理
heavy 资源密集型操作:剪枝、文档权限同步、外部组同步、CSV 生成
monitoring 系统健康监控与指标采集
user_file_processing 用户上传文件的索引与项目同步
scheduled_tasks 执行用户定时(Craft)任务
beat 周期任务调度器,使用 DynamicTenantScheduler 支持多租户

关键事实(源码确认于 app_base.py):

  • 所有 worker 使用线程池而非进程——因此 Celery 的 time limit 功能静默失效,超时逻辑必须在任务内部自行实现;
  • 多租户DynamicTenantScheduler 显式向每个 Beat 任务 kwargs 注入 tenant_id;直接发送的任务需自行传播,TenantAwareTask 在缺失时会静默回退到默认 schema(POSTGRES_DEFAULT_SCHEMA);
  • 队列与优先级:任务路由到命名队列并携带 High/Medium/Low 优先级(OnyxCeleryQueues / OnyxCeleryPriority,定义于 constants.py)。队列划分精细,例如 PORT(重索引队列,避免迁移饿死实时索引)、CHAT_TTL_DELETION(聊天 TTL 硬删除队列,避免清理饿死 check_for_indexing)等;
  • 任务定义规则:一律用 @shared_task 而非 @celery_app;任务放在 background/celery/tasks/ee/background/celery/tasks发送任务必须带 expires=,防止队列无限增长。

代码质量:pre-commit 与严格类型

# 安装并运行 pre-commit 钩子
pre-commit install
pre-commit run --all-files

# 更快的做法:只对你改动的文件运行
pre-commit run --files <path> [<path> ...]

两条全局铁律:

  1. 一切必须严格类型化(Python 与 TypeScript 皆如此);
  2. 代码注释保持简短,只写长期有效、面向未来读者的信息。

backend/AGENTS.md 还能看到配套约束:禁用 getattr(会躲开类型检查器),需要真正动态查找时必须加 # ods: ignore[getattr] 注释并说明理由(由 ods check-getattr 检查)。前端的格式化为 oxfmt、lint 为 oxlint,均由 bun install 安装,pre-commit 会自动对改动文件运行。

技术写作规范:ASD-STE100 简化技术英语

所有 prose(文档、commit message、PR 描述、报告、回复)统一遵循 ASD-STE100 Simplified Technical English

  • 只用已批准的词汇,每个词只有一个含义;
  • 一个概念用一个词表达,不用两个词指代同一事物;
  • 短句写作,指令句不超过 20 词;
  • 使用主动语态:写 "Turn the switch",而不是 "The switch must be turned";
  • 段落短小,每段只讲一个主题;
  • 代码注释聚焦长期相关或面向未来读者的信息。

这套规范保证了 AI Agent 与人类协作时沟通信息的一致性,也是该仓库"为 Agent 友好而设计"的体现。

四层测试体系:从单元到端到端

仓库共有 4 类测试,按范围递增:

类型 位置 前置条件 运行命令
单元测试 Unit backend/tests/unit 不假设任何 Onyx/外部服务可用,用 unittest.mock mock 外部交互 uv run pytest -xv backend/tests/unit
外部依赖单元测试 External Dependency Unit backend/tests/external_dependency_unit 假设 Postgres、Redis、MinIO/S3、OpenSearch 均在运行,OpenAI 可调用、外网可访问;但 Onyx 容器不运行,直接调用被测函数 uv run --env-file .vscode/.env pytest backend/tests/external_dependency_unit
集成测试 Integration backend/tests/integration 完整的真实 Onyx 部署在运行,不可 mock 任何东西 uv run --env-file .vscode/.env pytest backend/tests/integration
Playwright E2E web/tests/e2e 全部 Onyx 服务(含 Web Server)运行,TypeScript 编写 cd web && bun run playwright <TEST_NAME>

各层要点(依据 backend/tests/README.md):

  • 优先级:能写集成测试就优先写集成测试(或需要 mock/内部验证时写外部依赖单元测试),单元测试只留给复杂、隔离的模块(如 citation_processing.py);
  • 示例:外部依赖单元测试的优秀范例是 backend/tests/external_dependency_unit/connectors/confluence/test_confluence_group_sync.py;集成测试的优秀范例是 backend/tests/integration/tests/streaming_endpoints/test_chat_stream.py
  • 并行:集成测试按目录级别并行;
  • fixture 优先:写集成测试时优先使用 Manager 类工具(backend/tests/integration/common_utils)与根 conftest.py 的 fixture(如用 admin_user fixture 而非手动 UserManager.create(name="admin_user"));
  • 真实 LLM 调用的模型选择:OpenAI 用 gpt-5-mini(绝不使用 gpt-4o/gpt-4o-mini),Anthropic 用 claude-haiku-4-5
  • 重复运行找 flaky:可用 pytest-repeat,如 pytest --count=50 -x backend/tests/unit/path/to/test.py::test_name
  • EE 模式:使用 enable_ee fixture 而非内联 global_version.set_ee();注意 conftest.py 中的 pytestmark 不会作用到该目录下的测试,应使用 autouse fixture 包装模式。

Playwright 运行提示:文档明确要求使用 bun run playwright 脚本而非 bunx/npx——后者可能静默拉取未锁定版本的 Playwright。E2E 规格书写规则(Page Object Model、locator 优先级)见 web/tests/e2e/README.md

日志机制

在(1)编写集成测试或(2)进行活体测试(如 curl / playwright)时,可通过 backend/log/<service_name>_debug.log 获取日志。所有 Onyx 服务(api_server、web_server、celery_X)都会把日志 tail 到这个文件。这为调试与验证提供了统一入口。

安全注意事项

  • 绝不向仓库提交 API Key 或密钥;
  • 连接器凭据必须使用加密凭据存储;
  • 新功能遵循现有 RBAC 模式。

这些原则与 backend/AGENTS.md 的错误处理规范互为补充:业务代码一律抛出 OnyxError(来自 onyx.error_handling.exceptions)而非 HTTPException,由全局 FastAPI 异常处理器转换为标准 JSON 响应 {"error_code": "...", "detail": "..."}。错误码先定义在 error_codes.py,禁止临时发明。上游服务动态状态码场景使用 status_code_override 参数。其底层实现在 exceptions.py

from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError

# ✅ 推荐
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Session not found")

# ✅ 无需额外消息
raise OnyxError(OnyxErrorCode.UNAUTHENTICATED)

# ✅ 上游服务动态状态码
raise OnyxError(OnyxErrorCode.BAD_GATEWAY, detail, status_code_override=upstream_status)

# ❌ 禁止:直接使用 HTTPException / starlette / fastapi.status 常量

另外,所有 LLM、embedding、rerank、图像生成、语音(STT/TTS)、意图分类调用都必须打开带 LLMFlow 标签的 generation span(注册表在 flows.py),确保每次 LLM 调用可追踪、可观测。

编写实现计划(Plan)

plans 目录(已 gitignore,不存在则创建)编写计划时,至少包含以下要素:

  • Issues to Address:这次改动要解决什么问题;
  • Important Notes:研究过程中对实现重要的发现;
  • Implementation strategy:高层级的实现策略;
  • Tests:计划编写哪些单元(少用)、外部依赖单元、集成与 Playwright 测试来验证正确行为。不要过度测试——通常一个改动只需一种类型的测试。

明确禁止在计划中出现的:Timeline(时间线)、Rollback plan(回滚计划)。这是最小清单,可自由补充。计划中不写代码,保持高层级,可以引用特定文件或函数。写计划前必须做研究、探索代码库相关部分。

工程最佳实践:与 CONTRIBUTING.md 的呼应

CLAUDE.md 指出,工程最佳实践详见 CONTRIBUTING.md 的 "Engineering Best Practices" 章节,核心要点包括:

原则与协作

  • 1-way vs 2-way doors 决策模型:可逆决策快速迭代,不可逆决策深思熟虑;
  • 一致性优先于"正确":保持代码库统一模式,真正糟糕的模式则全局修复;
  • 修复你触及的代码(选择性):不引入新坏实践,顺手修复被改动代码暴露的问题;
  • 不堆砌功能:新增功能时按需重构,避免接口混乱与技术债累积。

风格与可维护性

  • 全仓库严格类型化,用 cast 处理松散类型接口;
  • 偏好 Pydantic 而非 dataclass;偏好显式 None 而非哨兵空字符串;用字符串枚举而非整数编码;杜绝 magic number 与 magic string;
  • 命名:优先长且描述性的函数名(便于代码搜索),同一对象在调用栈中保持一致命名;
  • 组合与函数式风格优先于继承/OOP;状态对象应意图明确、显式、尽量不可变;
  • 禁止死代码与注释掉的代码;禁止重复逻辑(LLM 常产生细微的重复逻辑,需仔细审查)。

性能与正确性

  • 避免长期持有资源(DB session、锁/信号量);
  • 连接器代码中任何可无界增长的内存结构必须周期性检查大小(连接器 OOM 常表现为 "missing celery tasks");
  • 绝不新增 async/event loop Python 代码,并尽量将现有 async 代码同步化——除非完全理解且确有必要。

仓库约定

  • Pydantic + 数据模型放 models.py;DB 接口函数放 db/ 目录;LLM prompts 放 prompts/ 目录;API 路由放 server/ 目录;
  • SQLAlchemy 注意 lazy loading 在规模下的开销与"session 外访问属性失败"风险;
  • Trunk-based development:PR 净改动不超过 500 行、频繁合并 main、用短生命周期 feature flag 增量上线、flag 加在 API/UI 入口层而非业务逻辑深处、同时测试 flag 开/关两种状态;
  • 新增 TODO 必须附带负责人姓名或 issue 编号;
  • 避免模块级 import 副作用;可执行脚本放 backend/scripts/ 并包含 if __name__ == "__main__": 块。

总结:一份可执行的 Agent 上手指南

综合 CLAUDE.md 与其引用的各子项目规范,在 Onyx 仓库中进行 AI Agent 开发的完整工作流可以浓缩为:

  1. uv sync --frozen 建立 .venv,复制 .vscode/env_template.txt.vscode/.env 并填写密钥;
  2. 确认所有 Onyx 服务运行(检查 backend/log 的日志输出),必要时用 docker exec onyx-relational_db-1 psql 连接 Postgres 验证数据;
  3. 调用后端一律经 http://localhost:3000 前端代理;用 Playwright 时使用 admin_user@example.com 账号;
  4. 修改代码遵守严格类型、pre-commit(ruff/oxfmt/oxlint)、ASD-STE100 写作规范;
  5. 测试按"集成测试优先"原则选择层级,用 @pytest.mark.secrets(TestSecret.X) 声明密钥、uv run 执行 pytest、bun run playwright 执行 E2E;
  6. 后台任务改动需用户重启 Celery worker(无热重载机制),迁移用 uv run alembic upgrade head(多租户加 -n schema_private);
  7. 动手前先在 plans/ 写出包含 Issues/Notes/Strategy/Tests 四要素的简短计划,再进入实现。

掌握上述工作流后,你便可以在 Onyx 的社区版核心、企业版扩展、前端、移动端与测试体系中高效、规范地开展工作。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
858
1.35 K
docsdocs
暂无描述
Markdown
899
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
923
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.83 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
532
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
524
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
393