{"_id":"@bndynet/ragbox","_rev":"4-85d417461bf5d98feb864df91f3b906b","name":"@bndynet/ragbox","dist-tags":{"latest":"1.0.0"},"versions":{"0.1.0":{"name":"@bndynet/ragbox","version":"0.1.0","_id":"@bndynet/ragbox@0.1.0","maintainers":[{"name":"bndy","email":"zb@bndy.net"}],"homepage":"https://github.com/bndynet/ragbox#readme","bugs":{"url":"https://github.com/bndynet/ragbox/issues"},"bin":{"ragbox":"dist/src/cli.js"},"dist":{"shasum":"5bdc1dfe381ee57d7b9b57a0d3b73824b576fd6d","tarball":"https://registry.npmjs.org/@bndynet/ragbox/-/ragbox-0.1.0.tgz","fileCount":45,"integrity":"sha512-si186CwtgjlAfCwBcEtOBVUV30N7GebVOZaKGROB2IQ7BsJK6FPLuJQRUnpPLwx1Z6o80uz4zyNjB7DFd5l+Pg==","signatures":[{"sig":"MEUCIC64D01PhCTAhRSoqoD2rbs9RyPexi5YAoqDHWgYlVGVAiEAifTqWDgMqd/mLeDitLDMW7/4J1kLttXTVDk4uDTo6mk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bndynet%2fragbox@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":260407},"main":"./dist/src/index.js","types":"./dist/src/index.d.ts","exports":{".":{"types":"./dist/src/index.d.ts","default":"./dist/src/index.js","require":"./dist/src/index.js"}},"gitHead":"b1984743ddb4137a9024e31ff195eef90fb715be","scripts":{"test":"npm run test:unit","build":"tsc -p tsconfig.json","ragbox":"node dist/src/cli.js","test:unit":"tsc -p tsconfig.json && node --test dist/test/folder-index.test.js"},"_npmUser":{"name":"bndy","email":"zb@bndy.net"},"repository":{"url":"git+https://github.com/bndynet/ragbox.git","type":"git"},"_npmVersion":"11.13.0","description":"RAG toolbox CLI for folder-level PageIndex indexing and querying.","directories":{},"_nodeVersion":"24.16.0","dependencies":{"chokidar":"^3.6.0","commander":"^12.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.0","@types/node":"^24.0.0"},"_npmOperationalInternal":{"tmp":"tmp/ragbox_0.1.0_1782124057711_0.5132077514413558","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@bndynet/ragbox","version":"0.1.1","_id":"@bndynet/ragbox@0.1.1","maintainers":[{"name":"bndy","email":"zb@bndy.net"}],"homepage":"https://github.com/bndynet/ragbox#readme","bugs":{"url":"https://github.com/bndynet/ragbox/issues"},"bin":{"ragbox":"dist/src/cli.js"},"dist":{"shasum":"7be80f6f39260bc2435b95b8e378dda0ebc3d262","tarball":"https://registry.npmjs.org/@bndynet/ragbox/-/ragbox-0.1.1.tgz","fileCount":45,"integrity":"sha512-4BhZRakJFyc7XVQsZdyerR9d3ySIOAS5g2YMazM4SYd+zAsgBTHmJgh5+uQqZlVDob1LBmKpBS/S8N//viF5Aw==","signatures":[{"sig":"MEYCIQDopGYS+Rk0L6OoLrw6RAgFny6hFswGKVHm/aobT8+3qgIhAPppd+K53PHU1bIdZFv0+IcGUhymlApYc7sEtdIWB8g1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bndynet%2fragbox@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":300844},"main":"./dist/src/index.js","types":"./dist/src/index.d.ts","exports":{".":{"types":"./dist/src/index.d.ts","default":"./dist/src/index.js","require":"./dist/src/index.js"}},"gitHead":"ad7b889022cf957f8c0c299b4f968b5d82d8bf70","scripts":{"test":"npm run test:unit","build":"tsc -p tsconfig.json","ragbox":"node dist/src/cli.js","test:unit":"tsc -p tsconfig.json && node --test dist/test/folder-index.test.js"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bef6c255-ef93-41f8-b958-f5cd997e83c2"}},"repository":{"url":"git+https://github.com/bndynet/ragbox.git","type":"git"},"_npmVersion":"11.13.0","description":"RAG toolbox CLI for folder-level PageIndex indexing and querying.","directories":{},"_nodeVersion":"24.17.0","dependencies":{"chokidar":"^3.6.0","commander":"^12.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.0","@types/node":"^24.0.0"},"_npmOperationalInternal":{"tmp":"tmp/ragbox_0.1.1_1782435316198_0.8852923401813197","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@bndynet/ragbox","version":"0.2.0","_id":"@bndynet/ragbox@0.2.0","maintainers":[{"name":"bndy","email":"zb@bndy.net"}],"homepage":"https://github.com/bndynet/ragbox#readme","bugs":{"url":"https://github.com/bndynet/ragbox/issues"},"bin":{"ragbox":"dist/src/cli.js"},"dist":{"shasum":"9c9421021b9fbae718328bda5768056358dd0607","tarball":"https://registry.npmjs.org/@bndynet/ragbox/-/ragbox-0.2.0.tgz","fileCount":55,"integrity":"sha512-V+ezsNta1PJ3DnJR5KOLo65A53TNy1EW7gVuW/kYy7z7zRbOOzf3HUcdO1VLLYZHIL5N6jqdeJ8wjiAoDiPfJw==","signatures":[{"sig":"MEYCIQC+5WQQFEAxfyJ9tCGJWzP2D8rkFfIpGnHj2oMwlSsIKwIhALhPiJfw3Yg1x/UoKTAYEwuMsQfdg17vxeLxRubxnAWr","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIANQkUaCyzz+Yt8YVFTNVNSOQcTp6z9N4E+M5rFC9enJAiBldLTDy7MXLDGoL/K/CNLdqo8sSk/+i/wY57LbGthQNA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bndynet%2fragbox@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":325934},"main":"./dist/src/index.js","types":"./dist/src/index.d.ts","exports":{".":{"types":"./dist/src/index.d.ts","default":"./dist/src/index.js","require":"./dist/src/index.js"}},"gitHead":"2a8f1f919c59d4ee0b17c29d565f647a9b76450b","scripts":{"test":"npm run test:unit","build":"npm run clean && tsc -p tsconfig.json","clean":"node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"","ragbox":"node dist/src/cli.js","test:e2e":"npm run build && node --test dist/test/e2e.test.js","test:unit":"npm run build && node --test dist/test/folder-index.test.js"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bef6c255-ef93-41f8-b958-f5cd997e83c2"}},"repository":{"url":"git+https://github.com/bndynet/ragbox.git","type":"git"},"_npmVersion":"11.19.0","description":"RAG toolbox CLI for folder-level PageIndex indexing and querying.","directories":{},"_nodeVersion":"24.20.0","dependencies":{"chokidar":"^3.6.0","commander":"^12.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.0","@types/node":"^24.0.0"},"_npmOperationalInternal":{"tmp":"tmp/ragbox_0.2.0_1788941066220_0.2058738150649586","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"_id":"@bndynet/ragbox@1.0.0","bin":{"ragbox":"dist/src/cli.js"},"bugs":{"url":"https://github.com/bndynet/ragbox/issues"},"dist":{"shasum":"323fa07dea4b480f92344f9b292395b6700ab1a1","tarball":"https://registry.npmjs.org/@bndynet/ragbox/-/ragbox-1.0.0.tgz","fileCount":55,"integrity":"sha512-XnCihr+wfGChkJNacplJCYkAap7uvy6KdLgulTrvWLVCqq4rn6GAxwolmWxxF7ZVpJN62NdF/0DRLG+jcKKhZw==","signatures":[{"sig":"MEUCICpiOZ2ImuGe/L/ZDX4VaQQuWk2vX1N/G5icEg31xythAiEAmFyDLxIyLso2SmkSczFhzSpHKFDyBqVpaHjrix7zW4I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDWbwSZoIPQCv9hSxllome6ohW/OCz74N4aQ3yvWd3WxgIgYyVKYB0qAchppI5nflwmA1zcMTh0U1bMFZUkWNRkE4U="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bndynet%2fragbox@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":330931},"main":"./dist/src/index.js","name":"@bndynet/ragbox","types":"./dist/src/index.d.ts","exports":{".":{"types":"./dist/src/index.d.ts","default":"./dist/src/index.js","require":"./dist/src/index.js"}},"gitHead":"d4e136f64d214cbc2994b92ca1b5515a7c02ee03","scripts":{"test":"npm run test:unit","build":"npm run clean && tsc -p tsconfig.json","clean":"node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"","ragbox":"node dist/src/cli.js","test:e2e":"npm run build && node --test dist/test/e2e.test.js","test:unit":"npm run build && node --test dist/test/folder-index.test.js"},"version":"1.0.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:bef6c255-ef93-41f8-b958-f5cd997e83c2"}},"homepage":"https://github.com/bndynet/ragbox#readme","repository":{"url":"git+https://github.com/bndynet/ragbox.git","type":"git"},"_npmVersion":"11.19.0","description":"RAG toolbox CLI for folder-level PageIndex indexing and querying.","directories":{},"maintainers":[{"name":"bndy","email":"zb@bndy.net"}],"_nodeVersion":"24.20.0","dependencies":{"chokidar":"^3.6.0","commander":"^12.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.8.0","@types/node":"^24.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ragbox_1.0.0_1789783810591_0.21240185667031253"}}},"time":{"created":"2026-06-22T10:27:37.539Z","modified":"2026-09-19T02:10:11.065Z","0.1.0":"2026-06-22T10:27:37.846Z","0.1.1":"2026-06-26T00:55:16.323Z","0.2.0":"2026-09-09T08:04:26.336Z","1.0.0":"2026-09-19T02:10:10.698Z"},"bugs":{"url":"https://github.com/bndynet/ragbox/issues"},"homepage":"https://github.com/bndynet/ragbox#readme","repository":{"url":"git+https://github.com/bndynet/ragbox.git","type":"git"},"description":"RAG toolbox CLI for folder-level PageIndex indexing and querying.","maintainers":[{"name":"bndy","email":"zb@bndy.net"}],"readme":"# RAGbox 中文文档\n\n`ragbox` 可以让你从命令行、HTTP 服务或 Node.js 应用里直接询问 Markdown、MDX 和 PDF 文档。\n\n它会把文档目录变成本地可查询索引，适合产品文档、API 指南、运维 runbook、内部手册和 monorepo 多包文档。基础流程不需要部署向量数据库。\n\n适合这些场景：\n\n- 对本地 docs 目录提问\n- 查看一次回答用了哪些文档和章节\n- 文档变化时持续刷新索引\n- 通过 HTTP 把文档问答接进内部 backend\n- 在本地、CI 和容器里使用同一套索引/查询流程\n\n## 快速开始\n\n默认路径按“开箱即用”设计：安装 `ragbox` 后直接索引和提问，第一次执行索引类命令时会自动准备 PageIndex。\n\n```bash\n# 安装 CLI\nnpm install -g @bndynet/ragbox\n\n# 创建 docs 和模型配置\nragbox init\n```\n\n继续下一步前，先编辑 `ragbox.config.json`：填好模型配置，并把 `docs.rootDir` / `docs.outputDir` 改成你的文档目录和索引目录。\n\n```json\n{\n  \"version\": 1,\n  \"pageIndex\": {\n    \"concurrency\": 1\n  },\n  \"llm\": {\n    \"baseUrl\": \"https://api.openai.com/v1\",\n    \"model\": \"gpt-4o-mini\",\n    \"apiKey\": \"sk-...\"\n  },\n  \"serve\": {\n    \"authToken\": \"YOUR_RAGBOX_SERVE_TOKEN\",\n    \"host\": \"127.0.0.1\",\n    \"port\": 8787\n  },\n  \"docs\": {\n    \"rootDir\": \"./docs\",\n    \"outputDir\": \"./.ragbox-index\",\n    \"include\": [\"**/*.md\", \"**/*.mdx\", \"**/*.pdf\"]\n  }\n}\n```\n\n如果有多个文档目录，改用 [项目配置](#项目配置) 里的 `sources` 映射。然后索引和提问：\n\n```bash\n# 基于配置里的 docs/source 生成本地索引\nragbox index\n\n# 提问\nragbox query \"怎么配置认证？\"\n\n# 可选：文档变化时持续刷新索引\nragbox watch --jsonl\n```\n\n如果想用一个前台进程同时负责索引、watch 和 query HTTP API，使用 `start`：\n\n```bash\nragbox start\n```\n\n如果只是想临时脱离当前终端运行，可以加 `--background`：\n\n```bash\nragbox start --background\nragbox restart\nragbox stop\n```\n\n需要临时覆盖配置时，仍然可以显式传路径：\n\n```bash\nragbox index ./docs --output-dir ./.ragbox-index\nragbox query ./.ragbox-index \"怎么配置认证？\"\n```\n\n如果不想把凭证写进 JSON，同样也支持通过环境变量或命令参数传入模型配置：\n\n```bash\nexport OPENAI_API_KEY=sk-...\nexport OPENAI_BASE_URL=https://api.openai.com/v1\n\nragbox index ./docs \\\n  --output-dir ./.ragbox-index \\\n  --model gpt-4o-mini\n\nragbox query ./.ragbox-index \"怎么配置认证？\" \\\n  --api-key sk-... \\\n  --base-url https://api.openai.com/v1 \\\n  --model gpt-4o-mini\n```\n\n## PageIndex 自动初始化\n\n第一次执行需要构建索引的 `index`、`watch` 或 `start` 时，ragbox 会自动：\n\n- 创建 `./.ragbox/pageindex-venv`\n- 安装当前 ragbox 版本支持的精确 PageIndex 包版本\n- 使用 setup lock 串行化并发安装\n- 后续运行直接复用该环境\n- 项目已有 `.gitignore` 或检测到 `.git` 时，自动加入 `.ragbox/`\n\n`ragbox setup pageindex` 仍然保留，作为 CI 镜像、离线部署或选择基础 Python 的可选预安装命令。显式配置的外部 `pageIndex.python`、`PAGEINDEX_PYTHON` 或 `--pageindex-python` 只会被校验，ragbox 不会自动修改该环境；标准 `./.ragbox/pageindex-venv` 即使写在配置里，仍由 ragbox 自动管理。\n\n## 前置条件\n\n自动初始化需要：\n\n- Node.js 18 或更新版本\n- 带 `venv` 和 `pip` 的 Python 3，用于安装 PageIndex SDK\n- 一个包含 `.md`、`.mdx` 或带文本层 `.pdf` 的文档目录\n- 一个兼容 OpenAI `/chat/completions` 的模型服务\n- 模型服务 API key\n\n## 常见使用方式\n\n| 目标 | 使用方式 |\n| --- | --- |\n| 从零开始 | `npm install -g @bndynet/ragbox`，然后运行 `ragbox init` 和 `ragbox index` |\n| 先在一个 docs 目录试用 | `ragbox index ./docs --output-dir ./.ragbox-index`，然后 `ragbox query ./.ragbox-index \"...\"` |\n| 不想每次重复路径 | 使用 `ragbox init` 写入的 `ragbox.config.json` |\n| 多个文档目录一起查询 | 配置 `sources`，分别跑 `ragbox index --source <name>`，再用 `ragbox query --all-sources \"...\"` |\n| 调试回答质量 | `ragbox query --trace --json \"...\"` 或 `ragbox trace query \"...\"` |\n| 检查索引和 HTTP 服务状态 | `ragbox status ./.ragbox-index` |\n| 诊断本地配置问题 | `ragbox doctor` |\n| 编辑文档时自动更新索引 | `ragbox watch ./docs --output-dir ./.ragbox-index --jsonl` |\n| 跑完整本地服务流程 | `ragbox start --auth-token <token>` |\n| 只服务一个已经生成好的索引 | `ragbox serve ./.ragbox-index --auth-token <token>` |\n\n## 项目配置\n\n`ragbox init` 会创建 `ragbox.config.json`。补上模型凭证后，典型配置如下：\n\n```json\n{\n  \"version\": 1,\n  \"pageIndex\": {\n    \"concurrency\": 1\n  },\n  \"llm\": {\n    \"baseUrl\": \"https://api.openai.com/v1\",\n    \"model\": \"gpt-4o-mini\",\n    \"apiKey\": \"sk-...\"\n  },\n  \"serve\": {\n    \"authToken\": \"YOUR_RAGBOX_SERVE_TOKEN\",\n    \"host\": \"127.0.0.1\",\n    \"port\": 8787\n  },\n  \"docs\": {\n    \"rootDir\": \"./docs\",\n    \"outputDir\": \"./.ragbox-index\"\n  }\n}\n```\n\n如果你只想创建配置、不安装 PageIndex，也可以运行：\n\n```bash\nragbox init\nragbox init --docs-dir ./content --output-dir ./.idx\n```\n\n配置文件中的相对路径会按配置文件所在目录解析。Server 端部署可以把 `baseUrl`、`model`、`serve.host`、`serve.port` 和 `apiKey` 一起放在私有 `ragbox.config.json`，或按环境拆成 `ragbox.config.prod.json`。如果配置文件会提交到仓库或共享给他人，就不要写真实 `apiKey`，改用环境变量或 secret manager。\n\n只有一个文档源时，用顶层 `docs` 就够了，不需要传 `--source`。项目里确实有多个命名文档源时，再使用可选的 `sources` 映射。\n\n之后命令会自动使用配置里的 docs：\n\n```bash\nragbox index\nragbox query \"怎么配置认证？\"\nragbox watch --jsonl\nragbox start\nragbox --config ./ragbox.config.json index\n```\n\n如果有多个文档目录，在 `sources` 里给每个目录起一个名字。这个模式适合 monorepo、产品文档加 API 文档、多个 app/package 各自有 docs 的项目：\n\n```json\n{\n  \"version\": 1,\n  \"pageIndex\": {\n    \"concurrency\": 1\n  },\n  \"llm\": {\n    \"baseUrl\": \"https://api.openai.com/v1\",\n    \"model\": \"gpt-4o-mini\",\n    \"apiKey\": \"sk-...\"\n  },\n  \"serve\": {\n    \"authToken\": \"YOUR_RAGBOX_SERVE_TOKEN\",\n    \"host\": \"127.0.0.1\",\n    \"port\": 8787\n  },\n  \"sources\": {\n    \"ragbox\": {\n      \"rootDir\": \"./ragbox\",\n      \"outputDir\": \"./.ragbox-index/ragbox\",\n      \"include\": [\"**/*.md\", \"**/*.mdx\", \"**/*.pdf\"]\n    },\n    \"icharts\": {\n      \"rootDir\": \"./icharts\",\n      \"outputDir\": \"./.ragbox-index/icharts\",\n      \"include\": [\"**/*.md\", \"**/*.mdx\", \"**/*.pdf\"]\n    }\n  }\n}\n```\n\n这个多 source 目录结构的可运行示例在 `./examples/ragbox.config.json`。\n\n命名 source 分别索引，query 时可以全局查，也可以限定 source：\n\n```bash\nragbox index --source ragbox\nragbox index --source icharts\n\nragbox query \"ragbox start 是做什么的？\"\nragbox query --source ragbox \"query trace 是怎么工作的？\"\nragbox query --source ragbox,icharts \"这些项目如何处理运行时流程？\"\nragbox query --all-sources \"当前示例有哪些文档主题？\"\nragbox start --all-sources\n```\n\n配置了多个 source 时，`ragbox query \"...\"` 默认查询全部 source。`--all-sources` 是同样行为的显式写法；要缩小范围时再用 `--source`。\n\n也可以按环境拆配置文件：\n\n```bash\nragbox --config prod index\nragbox --config ./ragbox.config.prod.json query \"怎么部署？\"\n```\n\n## 配置\n\nServer 端使用时，建议把稳定配置集中写在 `ragbox.config.json`：PageIndex Python 可执行文件、docs 路径、serve host/port、LLM `baseUrl`、`model`，以及私有配置文件里的 `apiKey`。环境变量和命令参数仍然支持，适合覆盖配置、接 secret manager，或临时运行。\n\n配置解析优先级为：命令行参数、`ragbox.config.json`、环境变量、默认值。\n\n| 配置 | 环境变量 | 配置 / 命令参数 | 用于 | 默认值 |\n| --- | --- | --- | --- | --- |\n| Python 可执行文件 | `PAGEINDEX_PYTHON` | `pageIndex.python`, `--pageindex-python` | `index`, `watch`, `start` | 自动管理的 `./.ragbox/pageindex-venv` |\n| 输出目录 | `RAGBOX_OUTPUT_DIR` | `--output-dir` | `index`, `watch`, `start` | `<folder>/.pageindex` |\n| 并发数 | `PAGEINDEX_CONCURRENCY` | `pageIndex.concurrency`, `--concurrency` | `index`, `watch`, `start` | `1` |\n| API Base URL | `OPENAI_BASE_URL` | `--base-url` | `index`, `watch`, `query` | `https://api.openai.com/v1` |\n| API Key | `OPENAI_API_KEY` | `--api-key` | `index`, `watch`, `query` | query 必填，PageIndex 通常也需要 |\n| 模型 | `PAGEINDEX_MODEL`, `LLM_MODEL` | `--model` | `index`, `watch`, `query` | `gpt-4o-mini` |\n| Serve host | `RAGBOX_SERVE_HOST` | `serve.host`, `--host` | `start`, `serve`, `status`, `doctor` | `127.0.0.1` |\n| Serve port | `RAGBOX_SERVE_PORT` | `serve.port`, `--port` | `start`, `serve`, `status`, `doctor` | `8787` |\n| Serve token | `RAGBOX_SERVE_TOKEN` | `serve.authToken`, `--auth-token` | `start`, `serve` | 无 |\n| Watch debounce | `RAGBOX_WATCH_DEBOUNCE_MS` | `--debounce-ms` | `watch` | `500` |\n| Watch 重试次数 | `RAGBOX_WATCH_RETRY_ATTEMPTS` | `--retry-attempts` | `watch` | `0` |\n| Watch 重试延迟 | `RAGBOX_WATCH_RETRY_DELAY_MS` | `--retry-delay-ms` | `watch` | `1000` |\n| Watch 锁文件 | `RAGBOX_WATCH_LOCK_FILE` | `--lock-file` | `watch` | 无 |\n| Watch staging | `RAGBOX_WATCH_STAGING` | `--staging` | `watch` | 关闭 |\n| Watch staging 输出目录 | `RAGBOX_WATCH_STAGING_OUTPUT_DIR` | `--staging-output-dir` | `watch` | `<outputDir>.staging` |\n| Watch health 文件 | `RAGBOX_WATCH_HEALTH_FILE` | `--health-file` | `watch` | 无 |\n| Watch webhook | `RAGBOX_WATCH_WEBHOOK_URL` | `--webhook` | `watch` | 无 |\n\n私有 server 配置可以把 `llm.apiKey` 写进 JSON，让部署自包含。配置文件会提交、共享或交给平台 secret 管理时，不要在 JSON 里写真实 key，改用 `OPENAI_API_KEY`。`--api-key` 适合本地测试，但可能出现在 shell history 或进程列表里。\n\n## 命令说明\n\n这一节是命令参考。如果你是第一次使用，建议先看 [快速开始](#快速开始) 和 [常见使用方式](#常见使用方式)。\n\n### `ragbox setup pageindex`\n\n可选地把固定版本的 PageIndex SDK 预装到 `./.ragbox/pageindex-venv`，更新 `ragbox.config.json`，并把 `.ragbox/` 加入 `.gitignore`。普通本地使用不再要求执行此命令。\n\n```bash\nragbox setup pageindex\nragbox setup pageindex --python python3\nragbox setup pageindex --no-write-config\n```\n\n自动化场景可以使用 `--json`。如果项目用其它方式管理本地生成工具，可以使用 `--no-gitignore`。\n\n### `ragbox init`\n\n只创建 `ragbox.config.json`，不安装 PageIndex。适合你自行管理 Python 环境的场景。\n\n```bash\nragbox init\nragbox init --docs-dir ./content --output-dir ./.idx\nragbox init --output ./configs/ragbox.config.json --force\n```\n\n### `ragbox index <folder>`\n\n为 Markdown/MDX/PDF 文档目录生成或更新本地索引。使用 `query` 或 `serve` 前需要先执行它。\n\n```bash\nragbox index ./docs\nragbox index ./docs --output-dir ./.ragbox-index\nragbox index ./docs --output-dir ./.ragbox-index --json\nragbox index ./docs --output-dir /var/lib/ragbox/docs-index --concurrency 2\nragbox index ./docs --pageindex-python /opt/venvs/pageindex/bin/python\nragbox index ./docs --base-url https://api.openai.com/v1 --model gpt-4o-mini\n```\n\n它会扫描 `**/*.md`、`**/*.mdx` 和 `**/*.pdf`，计算文件 hash，只重新索引新增、修改、之前失败的文件，并跳过未变化的 ready 文件。\n\nMarkdown 和 MDX 继续使用 PageIndex 的 `md_to_tree` helper；PDF 使用包内官方 `PageIndexClient` 的本地 Flash 模式。本地 PDF 处理只提取文件已有的文本层，不执行 OCR，因此扫描版或纯图片 PDF 需要先做 OCR 再索引。\n\n同一次索引还会在 `manifest.json`、`root-tree.json` 旁边写入 `lexical-index.json`。它只为 ready 的 PageIndex 节点保存本地精确 token terms。标准的 `query`、`queryIndex`、CLI 和 HTTP API 路径不会使用这个 sidecar；只有定制集成显式启用 tree plus lexical retriever 时才会读取它。\n\n如果有文档索引失败，普通输出会继续把统计信息写到 stdout，并把失败文档路径和 PageIndex 错误写到 stderr。\n\n使用 `--json` 可以输出带版本号的机器可读结果，包含输出路径、统计信息和失败文档明细：\n\n```json\n{\n  \"version\": 1,\n  \"command\": \"index\",\n  \"rootDir\": \"/repo/docs\",\n  \"outputDir\": \"/repo/.ragbox-index\",\n  \"manifestPath\": \"/repo/.ragbox-index/manifest.json\",\n  \"rootTreePath\": \"/repo/.ragbox-index/root-tree.json\",\n  \"generatedAt\": \"2026-01-01T00:00:00.000Z\",\n  \"counts\": {\n    \"total\": 12,\n    \"ready\": 12,\n    \"failed\": 0,\n    \"added\": 12,\n    \"modified\": 0,\n    \"retryFailed\": 0,\n    \"unchanged\": 0,\n    \"deleted\": 0\n  },\n  \"failures\": []\n}\n```\n\n### `ragbox inspect [target]`\n\n查看索引里有哪些文档、每篇文档的状态和统计信息。适合确认“到底索引进去了什么”。\n\n```bash\nragbox inspect ./.ragbox-index\nragbox inspect --source ragbox\nragbox inspect --all-sources --json\n```\n\n### `ragbox status [target]`\n\n检查索引是否已经可以 query，并探测本机 HTTP 服务的 `/health` 是否可达。服务探测会先使用 `serve.host` / `serve.port`，再使用 `RAGBOX_SERVE_HOST` / `RAGBOX_SERVE_PORT`，默认是 `127.0.0.1:8787`。\n\n```bash\nragbox status ./.ragbox-index\nragbox status --all-sources\nragbox status --json\n```\n\n### `ragbox doctor [target]`\n\n检查本地配置、PageIndex CLI 路径、LLM 设置、API key 是否存在、索引是否有效，以及本机 ragbox HTTP 服务是否健康。\n\n```bash\nragbox doctor\nragbox doctor --source ragbox --json\nragbox doctor --all-sources\n```\n\n### `ragbox query [target] <question>`\n\n基于 docs 目录或已有索引目录回答问题。如果传 docs 目录，目录下需要有默认的 `.pageindex` 索引。\n\n```bash\nragbox query ./docs \"怎么配置认证？\"\nragbox query ./.ragbox-index \"部署步骤是什么？\"\nragbox query ./docs/.pageindex \"怎么配置认证？\"\nragbox query ./.ragbox-index \"怎么配置认证？\" --model gpt-4o-mini --api-key sk-...\nragbox query ./.ragbox-index \"怎么配置认证？\" --json\nragbox query ./.ragbox-index \"怎么配置认证？\" --trace\nragbox trace query ./.ragbox-index \"怎么配置认证？\"\nragbox query \"部署步骤是什么？\"\nragbox query --source ragbox,icharts \"这些项目如何处理运行时流程？\"\nragbox query --all-sources \"部署步骤是什么？\"\n```\n\n这里建议使用和索引时相同的 `--base-url`，通常是 OpenAI-compatible 根地址，例如 `https://api.openai.com/v1`。如果某些代理只能提供完整接口地址，`query` 也兼容完整的 `/chat/completions` URL。\n\n显式传 target 时，第一个参数可以是：\n\n- docs 目录，里面有 `.pageindex/manifest.json` 和 `.pageindex/root-tree.json`\n- 索引输出目录，里面有：\n\n```text\nmanifest.json\nroot-tree.json\nindexes/\n```\n\n查询流程：\n\n1. 读取 `manifest.json` 和 `root-tree.json`\n2. 让 LLM 从文档树中选择相关文档\n3. 读取相关文档的 PageIndex JSON\n4. 去掉节点里的 `text` 字段，只让 LLM 基于结构选择相关节点\n5. 回到完整 JSON 中取出选中节点的 `text`\n6. 把这些文本拼成上下文，让 LLM 生成最终答案\n\n对多个配置 source，`ragbox query \"...\"` 默认查询全部 source。可以用 `--source` 传逗号分隔的名字来缩小范围，也可以用 `--all-sources` 显式表达全局查询。多源 query 会对每个 source 执行正常的结构化查询流程，然后让 LLM 基于各 source 选出的片段融合成一个最终回答。来源引用会加上 source 前缀，例如 `ragbox:start-command.md#n1`。\n\n使用 `--json` 可以输出带版本号的结果契约。单 source query 返回 `QueryResult`；多 source query 返回融合后的 `answer`、每个 source 的 `results`、带 source 前缀的 `sources`、`warnings` 和 `timingsMs`。\n\n单 source `QueryResult` 字段：\n\n- `answer`：最终回答文本\n- `contextBytes` 和 `contextTokens`：最终 answer context 的大小；tokens 为估算值\n- `selectedDocuments`：从 `root-tree.json` 中选中的文档，包含 `selectionReason`，必要时包含 `skipReason`\n- `selectedNodes`：每篇文档中选中的 PageIndex 节点，包含 `selectionReason`、可选 `skipReason` 和 `textBytes`\n- `sources`：最终回答使用的来源引用和节点文本\n- `warnings`：不可用文档、缺失节点或空上下文等提醒\n- `timingsMs`：解析、选择和生成回答的耗时\n- `trace`：只有使用 `--trace` 或 `ragbox trace query` 时才会出现，包含文档/节点选择阶段的 LLM 原始响应、prompt/response 字节数、context 大小和非致命失败记录\n\n致命 query 错误会带上失败阶段，例如 `Query failed during select-documents: ...`。\n\n### `ragbox start [folder]`\n\n运行完整本地服务流程：启动 watch、提供 HTTP query API，并持续刷新索引。\n\n```bash\nragbox start\nragbox start --auth-token dev-token\nragbox start --host 127.0.0.1 --port 8787 --jsonl\nragbox start --background\nragbox start --source ragbox\nragbox start --all-sources\nragbox start ./docs --output-dir ./.ragbox-index\n```\n\n需要用一个前台进程跑本地开发、内网服务或容器时，优先使用 `start`；缺少 PageIndex 时它会自动准备托管环境。HTTP `serve` 会在 watcher 注册后立即启动，所以初始索引还在运行时，`/` 和 `/health` 已经可以响应。`/health` 在首个索引快照可查询前返回 503；初始索引完成后，以及之后每次 watch 成功更新索引，都会刷新 serve 里的索引快照。\n\n传入 `--background` 时，`start` 会脱离当前终端后台运行。后台进程默认把 stdout/stderr 写到 `./ragbox.log`，并把 PID 写到 `./ragbox.pid`。可以用 `--log-file <path>` 和 `--pid-file <path>` 覆盖路径；如果不想写 PID 文件，可以传 `--no-pid-file`。\n\n在同一个工作目录运行 `ragbox stop`，会读取 `./ragbox.pid` 并停止对应后台进程。如果启动时用了自定义 pid 文件，停止时也传同一个 `--pid-file <path>`。\n\n配置了多个 source 时，`ragbox start` 默认启动全部 source。可以用 `--source ragbox,icharts` 限定范围，也可以用 `--all-sources` 显式表达全局启动。\n\n`start` 不会创建或修改 `ragbox.config.json`；需要持久化项目配置时使用 `ragbox init`。\n\n### `ragbox stop`\n\n读取当前工作目录的 `./ragbox.pid`，停止由 `ragbox start --background` 启动的后台进程。\n\n```bash\nragbox stop\nragbox stop --pid-file /var/run/ragbox.pid\nragbox stop --force\n```\n\n默认发送 `SIGTERM`，等待进程退出后删除 pid 文件。传 `--force` 时发送 `SIGKILL`。\n\n### `ragbox restart [folder]`\n\n读取 pid 文件，停止已有的 `ragbox start --background` 进程，然后用当前命令重新启动一个新的后台 `start` 进程。\n\n```bash\nragbox restart\nragbox restart --pid-file /var/run/ragbox.pid --log-file /var/log/ragbox.log\nragbox --config ./ragbox.config.prod.json restart\n```\n\n`restart` 是 `ragbox stop && ragbox start --background` 的便捷封装。它不会记住上一次 `start --background` 临时传入的参数；长期稳定配置建议放在 `ragbox.config.json`，或者在 `restart` 时重新传入同样的覆盖参数。\n\n### `ragbox serve [target]`\n\n启动一个前台 HTTP 服务，供外部系统通过 REST API 查询文档。使用前先通过 `ragbox index` 生成索引，或者用 `ragbox watch` 持续刷新索引。\n\n```bash\nragbox serve ./.ragbox-index \\\n  --host 127.0.0.1 \\\n  --port 8787 \\\n  --auth-token dev-token\n```\n\n多 source 项目可以直接基于配置文件启动：\n\n```bash\nragbox serve --config ./ragbox.config.json --host 0.0.0.0 --port 8787\n```\n\nPublic HTTP contract：\n\n- `GET /`：公开服务入口，返回 health 摘要和 endpoint 列表。\n- `GET /health`：公开 readiness endpoint，适合 load balancer、Kubernetes、systemd 和 smoke check。所有已知索引都可 query 时返回 200，否则返回 503。\n- `GET /indexes`：返回当前服务端缓存的索引校验快照。配置 token 后需要 `Authorization: Bearer <token>`。\n- `POST /query`：基于单个 target、选定 source 或全部 source 回答问题。配置 token 后需要鉴权。\n- `POST /reload`：重新读取 config/source target，并刷新服务端索引校验快照。配置 token 后需要鉴权。\n\n单索引请求：\n\n```bash\ncurl http://127.0.0.1:8787/\ncurl http://127.0.0.1:8787/health\nragbox status ./.ragbox-index\n\ncurl -H \"Authorization: Bearer dev-token\" \\\n  http://127.0.0.1:8787/indexes\n\ncurl -X POST http://127.0.0.1:8787/query \\\n  -H \"Authorization: Bearer dev-token\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"question\":\"怎么配置认证？\",\"trace\":true}'\n```\n\n多 source 请求：\n\n```bash\ncurl -X POST http://localhost:8787/query \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"source\":\"ragbox\",\"question\":\"ragbox start 是做什么的？\"}'\n\ncurl -X POST http://localhost:8787/query \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"source\":[\"ragbox\",\"icharts\"],\"question\":\"这些项目如何处理运行时流程？\"}'\n\ncurl -X POST http://localhost:8787/query \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"allSources\":true,\"question\":\"当前示例有哪些文档主题？\"}'\n\ncurl -X POST http://localhost:8787/reload\n```\n\n`serve` 首版面向本地服务、内网服务、container sidecar 和 docs backend。不要把 `.ragbox-index` 作为静态目录直接暴露，因为里面可能包含源文档正文。浏览器 widget 不应该直接携带 ragbox token；建议先请求自己的 backend，由 backend 负责用户登录、限流和审计，再转发给 `ragbox serve`。生产环境建议绑定 localhost 或内网地址，并配置 `serve.authToken`、`--auth-token` 或 `RAGBOX_SERVE_TOKEN`。\n\n### `ragbox watch <folder>`\n\n先执行一次索引，然后监听文档变化并增量更新。\n\n```bash\nragbox watch ./docs\nragbox watch ./docs --output-dir ./.ragbox-index\nragbox watch ./docs --output-dir /var/lib/ragbox/docs-index --concurrency 2\nragbox watch ./docs --base-url https://api.openai.com/v1 --model gpt-4o-mini\nragbox watch ./docs --output-dir ./.ragbox-index --jsonl\nragbox watch ./docs \\\n  --output-dir /var/lib/ragbox/docs-index \\\n  --staging \\\n  --retry-attempts 3 \\\n  --retry-delay-ms 2000 \\\n  --lock-file /var/run/ragbox/docs.lock \\\n  --health-file /var/run/ragbox/docs-health.json \\\n  --jsonl\n```\n\n`watch` 监听 `.md`、`.mdx` 和 `.pdf` 文件的新增、修改、删除。它会忽略 `node_modules`、`.git`、`.pageindex`、`dist`、`build`，以及位于文档目录内的自定义输出目录。\n\n使用 `--jsonl` 可以为集成场景输出带版本号的 JSON Lines 事件流。事件包括 `watch-start`、`watch-lock-acquired`、`watch-file-event`、`watch-index-start`、`watch-index-retry`、`watch-index-partial-failure`、`watch-output-promoted`、`watch-index-done`、`watch-index-failed`、`watch-health`、`watch-webhook-failed`、`watch-lock-released`、`watch-stop` 和 `index-progress`。\n\n生产化 watch 选项：\n\n- `--retry-attempts` 和 `--retry-delay-ms` 会重试抛错的索引运行，以及仍然留下 failed document 的运行。\n- `--lock-file` 会在 watch 运行期间创建排他锁文件。第二个 watcher 如果发现锁已存在，会直接退出。\n- `--staging` 会先索引到 staging 目录，只有在零 failed document 的干净运行后才 promote 到 active output。默认 staging 目录是 `<outputDir>.staging`；为了基于 rename 切换，建议和 `outputDir` 放在同一文件系统。\n- `--health-file` 会写 readiness JSON，包含 `status`、`ok`、`pid`、`lastSuccessAt`、`lastFailureAt` 和最新统计。\n- `--webhook` 会把每个 watch 事件作为 JSON POST 出去。Webhook 投递失败会报告为 `watch-webhook-failed` 事件，不会中断 watch。\n- `--debounce-ms` 控制文件变化后等待多久再重新索引。\n\n`ragbox watch` 有意以前台进程运行，这更适合 systemd service 或 container。建议使用 supervisor/container 的 restart policy，而不是在 CLI 里自行 fork daemon。\n\n## 输出目录\n\n默认输出：\n\n```text\ndocs/.pageindex/\n  manifest.json\n  root-tree.json\n  indexes/\n    <stable-doc-id>.pageindex.json\n  state/\n    file-state.json\n```\n\n自定义输出：\n\n```bash\nragbox index ./docs --output-dir ./.ragbox-index\nragbox query ./.ragbox-index \"...\"\n```\n\n输出目录可能包含源文档正文和元数据。如果文档是私有的，不要把输出目录公开暴露。\n\n## 生产使用建议\n\n常见方式：\n\n- 一个前台进程要同时负责索引、watch 和 serve 时，直接运行 `ragbox start`\n- 部署时执行 `ragbox index`，再通过 `ragbox serve` 或 SDK 查询完成后的索引目录\n- 文档会独立变化时，把 `ragbox watch` 作为后台服务运行\n\n建议：\n\n- 长期运行 `watch` 时，优先使用 `--jsonl`、`--lock-file`、`--health-file`、`--retry-attempts` 和 `--staging`\n- 把输出目录放在源码目录外，例如 `/var/lib/ragbox/docs-index`\n- 多副本应用需要读取同一份完整索引，可以挂载只读卷或随部署产物分发\n- API key 可以放私有 server 配置、环境变量或 secret manager；不要提交真实 key\n- 当 `serve` 不只绑定 localhost 时，使用 `serve.authToken`、`RAGBOX_SERVE_TOKEN` 或 `--auth-token`；如果配置会提交或共享，要把 token 当作密钥处理\n- 先用 `--concurrency 1`，确认 PageIndex 和模型服务限流后再提高\n- 生产镜像运行时不能联网时，提前执行 `ragbox setup pageindex`；普通本地运行会自动安装固定版本\n- 如果要求零停机更新，可以先索引到 staging 目录，成功后再切换读目录\n\n私有 server 配置示例：\n\n```json\n{\n  \"version\": 1,\n  \"pageIndex\": {\n    \"python\": \"/opt/pageindex-venv/bin/python\",\n    \"concurrency\": 1\n  },\n  \"llm\": {\n    \"baseUrl\": \"https://api.openai.com/v1\",\n    \"model\": \"gpt-4o-mini\",\n    \"apiKey\": \"sk-...\"\n  },\n  \"serve\": {\n    \"authToken\": \"YOUR_RAGBOX_SERVE_TOKEN\",\n    \"host\": \"127.0.0.1\",\n    \"port\": 8787\n  },\n  \"docs\": {\n    \"rootDir\": \"/srv/app/docs\",\n    \"outputDir\": \"/var/lib/ragbox/docs-index\"\n  }\n}\n```\n\n```bash\nragbox --config ./ragbox.config.prod.json index --concurrency 2\nragbox --config ./ragbox.config.prod.json query \"怎么配置认证？\"\n```\n\n### 后台运行\n\n本地或内网临时测试时，可以用 `ragbox start --background` 让同一个 start 流程脱离当前终端：\n\n```bash\nragbox --config ./ragbox.config.prod.json start \\\n  --background\n```\n\n```bash\nragbox --config ./ragbox.config.prod.json restart\nragbox stop\n```\n\n长期运行的 server 仍然建议交给进程管理器托管，这样进程崩溃后可以自动重启，启动顺序也更清晰。\n\nLinux server 推荐用 `systemd`：\n\n```ini\n[Unit]\nDescription=ragbox service\nAfter=network.target\n\n[Service]\nWorkingDirectory=/srv/ragbox\nExecStart=/usr/local/bin/ragbox --config ./ragbox.config.prod.json start\nRestart=always\nRestartSec=5\nEnvironment=NODE_ENV=production\n\n[Install]\nWantedBy=multi-user.target\n```\n\n```bash\nsudo systemctl enable ragbox\nsudo systemctl start ragbox\nsudo systemctl status ragbox\n```\n\n如果使用 Node 生态，也可以用 `pm2`：\n\n```bash\npm2 start \"ragbox --config ./ragbox.config.prod.json start\" --name ragbox\npm2 save\npm2 startup\n```\n\n容器部署时，让 `ragbox start` 保持为前台命令，然后使用平台的 restart policy，例如 Docker `--restart unless-stopped` 或 Kubernetes `restartPolicy`。\n\n## 在 Node.js 中使用 ragbox\n\n当你的 Node.js 服务需要创建索引、查询文档、校验索引，或以编程方式启动 `serve` 时，可以使用 SDK。\n\n```js\nconst {\n  createIndex,\n  inspectIndex,\n  queryIndex,\n  startServe,\n  validateIndex,\n  watchIndex\n} = require(\"@bndynet/ragbox\");\n\nawait createIndex(\"/srv/app/docs\", {\n  configPath: \"./ragbox.config.json\",\n  outputDir: \"/var/lib/ragbox/docs-index\",\n  pageIndexPython: \"/opt/pageindex-venv/bin/python\"\n});\n\nconst result = await queryIndex(\n  \"/var/lib/ragbox/docs-index\",\n  \"怎么配置认证？\"\n);\n\nconsole.log(result.answer);\nconsole.log(result.sources);\n\nconst validation = await validateIndex(\"/var/lib/ragbox/docs-index\");\nconsole.log(validation.ok);\n\nconst server = await startServe({\n  target: \"/var/lib/ragbox/docs-index\",\n  port: 8787,\n  authToken: process.env.RAGBOX_SERVE_TOKEN\n});\nconsole.log(server.url);\nawait server.close();\n\nconst inspect = await inspectIndex(\"/var/lib/ragbox/docs-index\");\nconsole.log(inspect.counts);\n\nconst watcher = await watchIndex(\"/srv/app/docs\", {\n  outputDir: \"/var/lib/ragbox/docs-index\",\n  pageIndexPython: \"/opt/pageindex-venv/bin/python\",\n  onEvent: (event) => console.log(event)\n});\nawait watcher.ready;\nawait watcher.close();\n```\n\n自定义 LLM client：\n\n```js\nconst { queryIndex, startServe } = require(\"@bndynet/ragbox\");\n\nconst llmClient = {\n  async chatCompletion(request) {\n    // request.messages, request.model, request.temperature\n    return await callYourModelGateway(request);\n  }\n};\n\nconst result = await queryIndex(\n  \"/var/lib/ragbox/docs-index\",\n  \"怎么配置认证？\",\n  {\n    llmClient,\n    model: \"internal-docs-model\"\n  }\n);\n\nconst server = await startServe({\n  target: \"/var/lib/ragbox/docs-index\",\n  llmClient,\n  model: \"internal-docs-model\",\n  port: 8787\n});\n```\n\n`llmClient` 是一个只用于 SDK 的薄 provider 边界，负责 query 阶段的直接 chat completion。它适合接本地模型、内部模型网关、重试、超时、日志和测试 mock。`ragbox` 不会从配置文件动态加载 provider 插件；CLI 仍然使用 flags、config 和环境变量里的 OpenAI-compatible 设置。\n\n包根入口导出稳定的产品化 SDK API。底层工具仍保留在 `advanced` namespace，适合更定制的集成：\n\n```js\nconst { advanced } = require(\"@bndynet/ragbox\");\n\nconst location = await advanced.resolveQueryIndexLocation(\"/var/lib/ragbox/docs-index\");\n```\n\n如果定制集成需要增强精确 token 召回，可以在 `advanced.queryFolder` 中显式启用 tree plus lexical retriever。默认 `queryIndex`、CLI 和 HTTP API 仍然使用标准 tree retriever。\n\n```js\nconst { advanced } = require(\"@bndynet/ragbox\");\n\nconst result = await advanced.queryFolder(\n  \"/var/lib/ragbox/docs-index\",\n  \"OPENAI_API_KEY 是在哪里配置的？\",\n  {\n    llmClient,\n    model: \"internal-docs-model\",\n    retriever: advanced.createTreeLexicalRetriever({\n      maxLexicalCandidates: 12\n    })\n  }\n);\n```\n\n如果定制集成需要控制 lexical index 体积，可以在用底层 `advanced.indexFolder` 建索引时传 `lexicalIndex`：\n\n```js\nawait advanced.indexFolder(\"/srv/app/docs\", {\n  outputDir: \"/var/lib/ragbox/docs-index\",\n  lexicalIndex: {\n    minTermLength: 2,\n    maxTermLength: 80,\n    maxTermsPerNode: 128\n  }\n});\n```\n\n`minTermLength` 默认是 `2`。`maxTermLength` 和 `maxTermsPerNode` 默认不限制。它们目前是底层 SDK 索引选项，还不是 CLI flag，也不是 `ragbox.config.json` 配置项。\n\n## 查询时发生了什么\n\n简单说，`ragbox` 会保留文档结构，而不是一开始就把所有内容切成匿名 chunk：\n\n- 每个 `.md`/`.mdx`/`.pdf` 文件会生成一棵结构化 PageIndex 树\n- 文档目录会生成一份索引清单\n- 查询时先选择可能相关的文档，再选择文档里的相关章节\n- 最终回答只基于选中的章节正文生成\n\n所以 `--trace` 能告诉你选了哪些文档和节点。基础流程也因此不需要部署向量数据库。\n\n## 与传统 Vector DB RAG 的对比\n\n传统 Vector RAG 通常会切 chunk、做 embedding，再按向量相似度召回。`ragbox` 则优先保留源文档层级，并让 LLM 基于这棵结构树做选择。\n\n| 维度 | Vector DB RAG | `ragbox` |\n| --- | --- | --- |\n| 索引单位 | 文本 chunk | Markdown/MDX/PDF 文件和 PageIndex 节点 |\n| 检索信号 | 向量相似度 | LLM 基于文档树和节点树选择 |\n| 存储 | 向量数据库加文档存储 | 输出目录下的本地 JSON 文件 |\n| 上下文形态 | 扁平 chunk 列表 | 带文件路径和 node id 的结构化节点 |\n| 优势 | 大规模模糊召回快 | 保留文档层级，引用来源更清晰 |\n| 取舍 | 需要 embedding 和索引基础设施 | 依赖 PageIndex 质量和 LLM 选择效果 |\n\n两种方式也可以组合：先用向量检索做大范围候选召回，再用 PageIndex 树做结构化过滤、上下文组织和引用生成。\n\n## 常见问题\n\n- `Failed to install pageindex`：检查 `python3`、`venv`、`pip` 和 Python 包源网络，或提前执行 `ragbox setup pageindex`\n- 显式 Python 报 `Unsupported PageIndex package version`：在该环境安装要求的版本，或移除 `pageIndex.python`、`PAGEINDEX_PYTHON`、`--pageindex-python`，改用自动管理环境\n- 报 `PDF has no extractable text layer`：先对扫描版或纯图片 PDF 执行 OCR，再索引 OCR 后的 PDF\n- `OPENAI_API_KEY is required for query`：在私有 `ragbox.config.json` 里添加 `llm.apiKey`，或设置 `OPENAI_API_KEY`，也可以临时传 `--api-key`\n- `Expected a docs folder... or a ragbox output directory`：`query` 的第一个参数可以传带 `.pageindex/` 的 docs 目录，也可以直接传索引输出目录\n\n## 限制\n\n- 首次索引会下载 PageIndex 依赖；离线环境需要提前执行 `ragbox setup pageindex`\n- 本地 PDF 索引要求文件带可提取的文本层，不会对扫描页执行 OCR\n- 查询质量依赖 PageIndex JSON 结构和所使用的 LLM\n- 当前基础流程是树结构选择，不是向量检索\n\n## 贡献者开发\n\n```bash\nnpm install\nnpm run build\nnpm run test:unit\nRAGBOX_E2E=1 npm run test:e2e\nnpm run ragbox -- --help\n```\n\n这个可选 E2E 会在临时项目中运行编译后的真实 CLI，隐式执行真实的 `pip install` 来安装固定版本的 PageIndex SDK，再通过 PageIndex 索引真实 Markdown 和带文本层 PDF，并分别查询生成的索引。PageIndex 摘要生成、ragbox 文档/节点选择和最终回答所需的模型调用，都会发到测试进程内的 OpenAI-compatible HTTP mock，因此不需要真实 API key，也不会产生模型费用；但全新安装 PageIndex 时仍需要能访问所配置的 Python 包源。\n\n### Examples\n\n可运行的本地 fixture 和 smoke-test 命令都放在 [`examples/README.md`](./examples/README.md)。需要用真实 PageIndex 和 LLM 配置验证 index、query、多 source 或 `start` 服务循环时，看那里即可。\n","readmeFilename":"README.zh-CN.md"}