{"owner":"OpenBMB","repo":"UltraRAG","hasSkills":true,"totalSkillsCount":3,"totalTokensCount":21494,"categories":["root-instruction","claude-rule"],"hasMcp":true,"mcpConfig":{"mcpServers":{"UltraRAG":{"command":"npx","args":["-y","@modelcontextprotocol/server-UltraRAG"]}}},"found":["AGENTS.md","docs/llms.txt","CLAUDE.md"],"skills":{"AGENTS.md":"# AGENTS.md\n\nThis document is the primary engineering guide for autonomous coding agents working in the `UltraRAG` repository.\n\nUse this file as the source of truth for architecture, conventions, workflows, and safe change patterns.\nIf `CLAUDE.md` exists, it should only point to this file.\n\n---\n\n## 1) Project Identity\n\n`UltraRAG` is a lightweight RAG framework built around the Model Context Protocol (MCP).\nThe key design choice is strict modularization: retrieval, prompting, generation, routing, memory, and evaluation are implemented as independent MCP servers orchestrated by YAML pipelines.\n\nCurrent core metadata:\n\n- Package: `ultrarag`\n- Version: `0.3.0`\n- Python: `>=3.11, <3.13`\n- CLI entrypoint: `ultrarag = ultrarag.client:main`\n- Package manager: `uv` (`[tool.uv] package = true`)\n\n---\n\n## 2) Repository Map (What Matters Most)\n\n```text\nUltraRAG/\n├── src/ultrarag/                    # Installable core package\n│   ├── client.py                    # CLI + pipeline engine + run/build orchestration\n│   ├── server.py                    # UltraRAG_MCP_Server (FastMCP extension)\n│   ├── api.py                       # Python API wrappers (ToolCall, PipelineCall)\n│   ├── cli.py                       # Rich banner and CLI visuals\n│   ├── mcp_logging.py               # Central logging setup\n│   ├── mcp_exceptions.py            # Node.js checks for remote MCP\n│   └── utils.py                     # Subprocess lifecycle helpers\n│\n├── servers/                         # MCP microservices (each server is independent)\n│   ├── retriever/\n│   ├── generation/\n│   ├── prompt/\n│   ├── reranker/\n│   ├── benchmark/\n│   ├── evaluation/\n│   ├── corpus/\n│   ├── memory/\n│   ├── router/\n│   ├── custom/\n│   ├── pageindex/\n│   └── sayhello/\n│\n├── examples/\n│   ├── demos/                       # UI-ready demo pipelines\n│   └── experiments/                 # Experiment/research pipelines\n│\n├── ui/\n│   ├── backend/                     # Flask backend + pipeline manager\n│   └── frontend/                    # Vite + React + TypeScript\n│\n├── docs/                            # Docs and assets\n├── script/                          # Utility scripts (deploy, case study, etc.)\n├── pyproject.toml                   # Dependencies + package metadata\n├── uv.lock                          # Locked dependency graph\n├── Dockerfile*                      # Container variants\n└── .gitignore\n```\n\nImportant generated/derived files:\n\n- `servers/*/server.yaml` (generated by each server `build` tool)\n- `examples/**/parameter/*_parameter.yaml` (pipeline-merged parameters)\n- `examples/**/server/*_server.yaml` (pipeline-merged server config)\n- `output/memory_*.json` (per-run memory snapshots)\n\n---\n\n## 3) Mental Model of the System\n\nThink of UltraRAG as a three-layer system:\n\n1. **Interface layer**: CLI (`ultrarag ...`), UI (`ultrarag show ui`), and Python API (`ToolCall`, `PipelineCall`)\n2. **Orchestration layer**: `src/ultrarag/client.py` (`build`, `load_pipeline_context`, `execute_pipeline`)\n3. **Execution layer**: MCP servers in `servers/*`, each exposing tools/prompts over stdio (or remote MCP proxy)\n\nThe runtime contract is:\n\n- A pipeline YAML declares **which servers** to use and **which steps** to execute.\n- The client resolves I/O dependencies between steps.\n- Each step calls exactly one MCP tool or prompt.\n- Outputs are saved to a shared variable pool and can feed downstream steps.\n\n---\n\n## 4) Two-Phase Execution Lifecycle\n\n### Phase A: Build\n\nCommand:\n\n```bash\nultrarag build <pipeline.yaml>\n```\n\nWhat happens:\n\n- Reads `servers:` from the pipeline YAML.\n- For each referenced server, calls the server's `build` tool.\n- Produces:\n  - `<pipeline_dir>/parameter/<pipeline_name>_parameter.yaml`\n  - `<pipeline_dir>/server/<pipeline_name>_server.yaml`\n\nWhy this matters:\n\n- `build` materializes exact tool/prompt I/O metadata before runtime.\n- UI and runner rely on these generated artifacts for deterministic execution.\n\n### Phase B: Run\n\nCommand:\n\n```bash\nultrarag run <pipeline.yaml> [--param path] [--is_demo]\n```\n\nWhat happens:\n\n- Loads generated server config + parameter config.\n- Creates `fastmcp.Client` transport config for each server.\n- Executes pipeline steps in order, including `loop` and `branch`.\n- Saves intermediate memory snapshots and writes `output/memory_*.json`.\n- Invokes cleanup tools (e.g., tools ending with `vllm_shutdown`) if present.\n\n---\n\n## 5) Pipeline DSL Reference\n\nUltraRAG accepts mixed step forms:\n\n### 5.1 Plain step\n\n```yaml\n- retriever.retriever_search\n```\n\n### 5.2 Step with input/output remapping\n\n```yaml\n- generation.generate:\n    input:\n      prompt_ls: custom_prompt_ls\n    output:\n      ans_ls: final_answer_ls\n```\n\n### 5.3 Loop block\n\n```yaml\n- loop:\n    times: 3\n    steps:\n    - retriever.retriever_search\n    - generation.generate\n```\n\n### 5.4 Branch block\n\n```yaml\n- branch:\n    router:\n    - router.route_query\n    branches:\n      need_retrieval:\n      - retriever.retriever_search\n      direct_answer:\n      - generation.generate\n```\n\n### 5.5 Prompt vs Tool step semantics\n\n- Steps under the `prompt` server call `client.get_prompt(...)`.\n- Non-prompt steps call `client.call_tool(...)`.\n\n---\n\n## 6) Variable Resolution and Data Flow Rules\n\nIn `UltraData`, each tool input value is interpreted by convention:\n\n- `\"$foo\"` -> load from server-local params (`parameter.yaml`)\n- `\"bar\"`  -> read from global variable pool (`global_vars[\"bar\"]`)\n- `\"memory_xxx\"` -> read/write memory lists\n\nOutput handling:\n\n- Tool returns JSON payload -> keys mapped to declared outputs\n- Output remapping (`output:` in pipeline step) is applied at save time\n- Prompt outputs usually produce `prompt_ls`\n\nBranch handling:\n\n- Branch-aware values use wrapped list records with branch-state keys.\n- Internal sentinel (`UNSET`) is used to distinguish \"not filled yet\" from `None`.\n\nMemory handling:\n\n- The engine tracks `memory_*` histories automatically.\n- Final snapshots are serialized to `output/memory_<...>.json`.\n- If a memory server is detected, turn-level memory auto-save can be triggered.\n\n---\n\n## 7) Core Python Modules (Authoritative Guide)\n\n### `src/ultrarag/client.py`\n\nThis is the orchestration heart of the project.\n\nKey responsibilities:\n\n- CLI entrypoint (`main`)\n- UI launch (`launch_ui`) and case-study launch (`launch_case_study`)\n- Config loading (`Configuration`)\n- Pipeline data graph and state (`UltraData`)\n- Build pipeline configs (`build`)\n- Load runtime context (`load_pipeline_context`)\n- Execute step engine (`execute_pipeline`)\n- Run full pipeline (`run`)\n\nImportant runtime behaviors:\n\n- Supports both local python MCP servers (`path.endswith(\".py\")`) and remote MCP endpoints (`http(s)`).\n- For remote MCP, requires Node.js >= 20 and uses `npx -y mcp-remote <url>`.\n- Keeps loop-termination state in `ContextVar` for coroutine safety.\n- Emits structured stream events in demo/UI flows (`step_start`, `step_end`, `token`, `sources`).\n\n### `src/ultrarag/server.py`\n\nDefines `UltraRAG_MCP_Server`, a compatibility wrapper over FastMCP.\n\nKey responsibilities:\n\n- Enhanced `tool()` and `prompt()` registration with `output` metadata support\n- Metadata capture for automatic config generation\n- `build(parameter_file)` to generate per-server `server.yaml`\n- Compatibility filtering for FastMCP signature differences\n\n### `src/ultrarag/api.py`\n\nProvides ergonomic Python-side wrappers:\n\n- `initialize(servers, server_root, log_level)`\n- `ToolCall.server_name.tool_name(...)`\n- `PipelineCall(pipeline_file, parameter_file, log_level)`\n\n### `src/ultrarag/mcp_logging.py`\n\n- Initializes root logger `UltraRAG`\n- Rich console logging + file logging (`logs/<timestamp>.log`)\n- Log level controlled by `log_level` argument and environment\n\n### `src/ultrarag/mcp_exceptions.py`\n\n- Validates local Node.js availability/version\n- Raises `NodeNotInstalledError` / `NodeVersionTooLowError`\n\n### `src/ultrarag/utils.py`\n\n- Subprocess lifecycle helpers\n- POSIX parent-death signal support\n- Windows job object support for child-process cleanup\n\n---\n\n## 8) MCP Server Authoring Contract\n\nEach server follows this shape:\n\n```text\nservers/<name>/\n├── parameter.yaml\n├── server.yaml            # generated\n└── src/<name>.py\n```\n\n### 8.1 Registration styles\n\nUse either:\n\n1. Decorator style\n2. Class-bound method registration style\n\nBoth are valid in this codebase.\n\n### 8.2 `output=` grammar\n\nCanonical form:\n\n```text\ninput1,input2,$param_a -> output1,output2\n```\n\nRules:\n\n- Left side maps function args to pipeline inputs.\n- Right side defines expected output keys from returned dict/JSON.\n- `-> None` means no output variables.\n- `$param` means value comes from server parameter file.\n\n### 8.3 Return payload expectations\n\n- For tools: return JSON-serializable dict payloads matching declared output keys.\n- For prompts: return prompt messages (typically list-like prompt payloads consumed by `get_prompt`).\n\n### 8.4 Entrypoint requirement\n\nServer modules should end with:\n\n```python\nif __name__ == \"__main__\":\n    app.run(transport=\"stdio\")\n```\n\n---\n\n## 9) Retriever/Generation/Prompt Specific Notes\n\n### Retriever (`servers/retriever`)\n\n- Supports multiple retrieval modes:\n  - Dense retrieval\n  - BM25\n  - Web search\n  - Project-memory retrieval\n- Index backends are pluggable via factory:\n  - `faiss`\n  - `milvus`\n  - `qdrant`\n- Web search backends are pluggable via factory:\n  - `exa`\n  - `tavily`\n  - `zhipuai`\n  - `serply`\n\n### Generation (`servers/generation`)\n\n- Supports generation backends including `openai`, `vllm`, and hf workflows.\n- Provides explicit cleanup tool `vllm_shutdown`.\n- Demo mode can use local streaming generation service.\n\n### Prompt (`servers/prompt`)\n\n- Uses `SandboxedEnvironment` from Jinja2 for safer rendering.\n- Validates template paths to reduce traversal risk.\n- Escapes string inputs before template rendering.\n\n---\n\n## 10) UI Backend Architecture (`ui/backend`)\n\n### `app.py`\n\n- Flask app factory (`create_app`)\n- Serves frontend static assets\n- Exposes chat/pipeline/KB/auth endpoints\n- Reads optional frontend override via `ULTRARAG_FRONTEND_DIR`\n\n### `pipeline_manager.py`\n\nThis module is large and central to UI behavior.\n\nMajor responsibilities:\n\n- Session lifecycle and streaming chat management\n- Background chat task management\n- Pipeline CRUD (list/load/save/rename/delete)\n- Parameter load/save/build wrappers\n- Knowledge base file ingest and pipeline triggering\n- Memory synchronization to per-user KB collections\n- Optional server introspection via AST stub generation if `server.yaml` is missing\n\nNotable behavior:\n\n- Applies defensive patches to suppress noisy closed-event-loop teardown logs.\n- Uses a queue bridge for async-to-sync SSE event streaming.\n- Supports automatic memory-to-KB sync for pipelines that include memory components.\n\n---\n\n## 11) Storage Model and Paths\n\nDefault UI storage root:\n\n- `ui/storage`\n\nCan be overridden by:\n\n- `ULTRARAG_UI_STORAGE_ROOT`\n\nKey subpaths:\n\n- `db/users.sqlite3`\n- `chat_sessions/`\n- `knowledge_base/raw|corpus|chunks|index`\n- `memory/`\n- `knowledge_base/_memory_sync`\n\nRuntime outputs:\n\n- `output/memory_*.json`\n- `logs/*.log`\n\n---\n\n## 12) Environment Variables You Should Know\n\n- `ULTRARAG_UI_STORAGE_ROOT`: override UI storage root\n- `ULTRARAG_FRONTEND_DIR`: override frontend static directory\n- `ULTRARAG_SESSION_TIMEOUT`: foreground chat session timeout\n- `ULTRARAG_BG_SESSION_TIMEOUT`: background session timeout\n- `ULTRARAG_LOG_TS`: custom timestamp seed for log file naming\n- `log_level`: consumed by core logger initialization\n\n---\n\n## 13) Dependency Model\n\nInstall tiers from `pyproject.toml`:\n\n- Core install: no extras\n- `retriever` extra\n- `generation` extra\n- `evaluation` extra\n- `corpus` extra\n- `all` extra (union)\n\nTypical commands:\n\n```bash\nuv sync\nuv sync --extra retriever\nuv sync --extra generation\nuv sync --all-extras\n```\n\nDevelopment dependencies include:\n\n- `ruff`\n- `ipython`\n- `jupyter`\n- `pytest`\n\n---\n\n## 14) CLI Commands (Canonical)\n\n```bash\nultrarag build <pipeline.yaml>\nultrarag run <pipeline.yaml> [--param <parameter.yaml>] [--log_level info|debug|warn|error] [--is_demo]\nultrarag show ui [--host 127.0.0.1] [--port 5050]\nultrarag show case [--config_path <memory.json>] [--host 127.0.0.1] [--port 8080]\n```\n\nMinimal smoke check:\n\n```bash\nultrarag run examples/experiments/sayhello.yaml\n```\n\n---\n\n## 15) Docker Variants\n\n- `Dockerfile`: full image (builds frontend, installs all extras)\n- `Dockerfile.base-cpu`: CPU base image\n- `Dockerfile.base-gpu`: GPU base image\n\nAll variants start UI with:\n\n```bash\nultrarag show ui --port 5050 --host 0.0.0.0\n```\n\n---\n\n## 16) Development Playbooks\n\n### 16.1 Add a new MCP server\n\n1. Create `servers/<name>/parameter.yaml`\n2. Implement `servers/<name>/src/<name>.py`\n3. Register tools/prompts via `app.tool` / `app.prompt` (or class-bound registration)\n4. Ensure `app.run(transport=\"stdio\")` exists\n5. Add server to `servers:` in a pipeline YAML\n6. Run `ultrarag build <pipeline.yaml>`\n\n### 16.2 Add a new tool to an existing server\n\n1. Implement function/method\n2. Register with explicit `output=...` contract\n3. Ensure return payload keys match outputs\n4. Update relevant parameter keys in `parameter.yaml` if using `$...`\n5. Add the step in pipeline YAML and rebuild\n\n### 16.3 Add a retriever index backend\n\n1. Implement backend in `servers/retriever/src/index_backends/`\n2. Follow `BaseIndexBackend` contract\n3. Register in `_INDEX_BACKENDS` map in `index_backends/__init__.py`\n\n### 16.4 Add a web-search backend\n\n1. Implement backend in `servers/retriever/src/websearch_backends/`\n2. Follow `BaseWebSearchBackend` contract\n3. Register in `_WEBSEARCH_BACKENDS` map\n\n### 16.5 Modify UI pipeline behavior\n\nPrimary files:\n\n- `ui/backend/app.py`\n- `ui/backend/pipeline_manager.py`\n\nIf touching build/runtime semantics, cross-check against:\n\n- `src/ultrarag/client.py`\n\n---\n\n## 17) Coding Standards (Repository-Conformant)\n\n- Use type hints for function signatures.\n- Prefer `pathlib.Path` for filesystem paths.\n- Use `yaml.safe_load` / `yaml.safe_dump`.\n- Use project logger (`get_logger` or `app.logger`) instead of `print`.\n- Keep tool outputs deterministic and JSON-serializable.\n- Keep imports grouped: stdlib -> third-party -> local.\n- Keep async boundaries explicit (`async`/`await`).\n\n---\n\n## 18) What Not To Edit Blindly\n\nTreat these as generated or runtime artifacts unless intentionally regenerating:\n\n- `servers/*/server.yaml`\n- `examples/**/parameter/*_parameter.yaml`\n- `examples/**/server/*_server.yaml`\n- `output/*`\n- `logs/*`\n- `ui/storage/*` runtime data\n\nAlso avoid committing secrets:\n\n- `.env`\n- any credential-bearing local config\n\n---\n\n## 19) Common Failure Modes and Fixes\n\n### Error: server file not found\n\n- Check `servers.<name>` path in pipeline YAML.\n- Ensure server entry script exists at `servers/<name>/src/<name>.py` (or update path in config).\n\n### Error: missing variable in pipeline execution\n\n- Verify output key names from upstream tool match downstream input names.\n- Verify remapping under step-level `output:` is correct.\n- Verify `$param` keys exist in that server's parameter config.\n\n### Remote MCP server fails to start\n\n- Ensure Node.js >= 20.\n- Confirm `npx` is available.\n- Confirm remote URL in server path is reachable.\n\n### Build succeeds but UI cannot list tools\n\n- Ensure `server.yaml` exists or can be inferred by AST parsing.\n- Check for unusual dynamic registration patterns that static analysis cannot infer.\n\n### No final answer in chat\n\n- Inspect `output/memory_*.json`.\n- Check whether generation step ran and produced `ans_ls`.\n- Check stream events in UI path (`step_start`, `step_end`, `sources`, `token`, `final`).\n\n---\n\n## 20) Validation Checklist for Agents\n\nBefore finalizing any non-trivial change:\n\n1. Build the affected pipeline:\n   - `ultrarag build <pipeline.yaml>`\n2. Run a relevant smoke case:\n   - `ultrarag run <pipeline.yaml>`\n3. If UI behavior changed:\n   - run `ultrarag show ui` and verify route behavior\n4. If retriever/generation backends changed:\n   - validate parameter schema keys and output names\n5. Keep generated artifacts intentional:\n   - do not accidentally commit transient runtime outputs\n\n---\n\n## 21) Minimal Quickstart for New Agents\n\n```bash\n# 1) Install dependencies\nuv sync --all-extras\n\n# 2) Smoke test\nultrarag run examples/experiments/sayhello.yaml\n\n# 3) Build and run a demo pipeline\nultrarag build examples/demos/LLM.yaml\nultrarag run examples/demos/LLM.yaml\n\n# 4) Launch UI\nultrarag show ui --host 127.0.0.1 --port 5050\n```\n\n---\n\n## 22) Final Notes\n\n- This repository is orchestration-first: correctness depends heavily on I/O naming consistency across tools and pipeline steps.\n- Most regressions come from mismatched variable names, stale generated configs, or incomplete parameter updates.\n- When in doubt, inspect `src/ultrarag/client.py` first: it is the execution truth.\n","docs/llms.txt":"This file is a merged representation of the entire codebase, combining all repository files into a single document.\nGenerated by Repopack on: 2026-01-18T09:46:51.430Z\n\n================================================================\nFile Summary\n================================================================\n\nPurpose:\n--------\nThis file contains a packed representation of the entire repository's contents.\nIt is designed to be easily consumable by AI systems for analysis, code review,\nor other automated processes.\n\nFile Format:\n------------\nThe content is organized as follows:\n1. This summary section\n2. Repository information\n3. Repository structure\n4. Multiple file entries, each consisting of:\n  a. A separator line (================)\n  b. The file path (File: path/to/file)\n  c. Another separator line\n  d. The full contents of the file\n  e. A blank line\n\nUsage Guidelines:\n-----------------\n- This file should be treated as read-only. Any changes should be made to the\n  original repository files, not this packed version.\n- When processing this file, use the file path to distinguish\n  between different files in the repository.\n- Be aware that this file may contain sensitive information. Handle it with\n  the same level of security as you would the original repository.\n\nNotes:\n------\n- Some files may have been excluded based on .gitignore rules and Repopack's\n  configuration.\n- Binary files are not included in this packed representation. Please refer to\n  the Repository Structure section for a complete list of file paths, including\n  binary files.\n\nAdditional Info:\n----------------\n\nFor more information about Repopack, visit: https://github.com/yamadashy/repopack\n\n================================================================\nRepository Structure\n================================================================\npages/\n  en/\n    api/\n      benchmark.mdx\n      cli.mdx\n      corpus.mdx\n      custom.mdx\n      evaluation.mdx\n      generation.mdx\n      prompt.mdx\n      reranker.mdx\n      retriever.mdx\n      router.mdx\n    demo/\n      deepresearch.mdx\n      lightresearch.mdx\n      llm.mdx\n      rag.mdx\n    develop_guide/\n      case_study.mdx\n      code_integration.mdx\n      dataset.mdx\n      debug.mdx\n      parallel.mdx\n    getting_started/\n      installation.mdx\n      introduction.mdx\n      quick_start.mdx\n      update.mdx\n    pipeline/\n      light_deepresearch.mdx\n      rag.mdx\n      search_o1.mdx\n      visrag.mdx\n    rag_client/\n      branch.mdx\n      data_and_params.mdx\n      loop.mdx\n      multi_agents.mdx\n      pipeline.mdx\n      serial.mdx\n    rag_servers/\n      benchmark.mdx\n      corpus.mdx\n      custom.mdx\n      evaluation.mdx\n      generation.mdx\n      overview.mdx\n      prompt.mdx\n      reranker.mdx\n      retriever.mdx\n      router.mdx\n    ui/\n      prepare.mdx\n      start.mdx\n\n================================================================\nRepository Files\n================================================================\n\n================\nFile: pages/en/api/benchmark.mdx\n================\n---\ntitle: \"Benchmark\"\nicon: \"chart-line\"\n---\n\n## `get_data`\n\n**Signature**\n```python\n@app.tool(output=\"benchmark->q_ls,gt_ls\")\ndef get_data(benchmark: Dict[str, Any]) -> Dict[str, List[Any]]\n```\n\n**Function**\n- Multi-format Loading: Supports loading evaluation datasets in `.jsonl`, `.json`, or `.parquet` formats from local storage.\n- Dynamic Field Mapping: Uses `key_map` to map different column names in raw data (such as `question`, `answer`) to standardized output keys (usually `q_ls` and `gt_ls`).\n- Data Preprocessing: Built-in support for random shuffling (`shuffle`) and quantity truncation (`limit`).\n- Used in Demos to receive user input, treating it as a piece of data (`q_ls`).\n\n**Output Format (JSON)**\n```json\n{\n  \"q_ls\": [\"Question 1\", \"Question 2\"],\n  \"gt_ls\": [[\"Answer A1\", \"Answer A2\"], [\"Answer B\"]]\n}\n```\n\n---\n\n## Configuration\n\n```yaml servers/benchmark/parameter.yaml icon=\"/images/yaml.svg\"\nbenchmark:\n  name: nq\n  path: data/sample_nq_10.jsonl\n  key_map:\n    q_ls: question\n    gt_ls: golden_answers\n  shuffle: false\n  seed: 42\n  limit: -1\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `name` | str | Evaluation set name, used only for logging and identification (Example: `nq`) |\n| `path` | str | Data file path, supports `.jsonl`, `.json`, `.parquet` |\n| `key_map` | dict | Field mapping table, mapping raw fields to tool output keys |\n|  | `q_ls` | str| Raw field name mapped to Question List (e.g., question column in file) |\n|  | `gt_ls` | str| Raw field name mapped to Golden Answer List (e.g., golden_answers column in file) |\n| `shuffle` | bool | Whether to shuffle sample order (default `false`) |\n| `seed` | int | Random seed (effective when `shuffle=true`) |\n| `limit` | int | Upper limit of sampled data items. Default is -1 (load all), positive integer means truncation of first N items |\n\n================\nFile: pages/en/api/cli.mdx\n================\n---\ntitle: \"CLI\"\nicon: \"command\"\n---\n\n## help — Command Overview\n\n```bash\nultrarag --help\n```\n\n```text\nusage: ultrarag [-h] {build,run,show} ...\n\nUltraRAG CLI\n\npositional arguments:\n  {build,run,show}\n    build           Build the configuration\n    run             Run the pipeline with the given configuration\n    show            Show ui interfaces\n\noptions:\n  -h, --help        show this help message and exit\n```\n\n---\n\n## build — Build Configuration\n\n**Purpose**: Automatically generate a unified parameter configuration file based on `pipeline.yaml`.\n\n```bash\nultrarag build <CONFIG> [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `<CONFIG>` | Pipeline configuration file path, e.g., `examples/pipeline.yaml` |\n| `--log_level` | Log level, default `INFO` |\n\n---\n\n## run — Execute Pipeline\n\n**Purpose**: Start Pipeline execution.\n\n```bash\nultrarag run <CONFIG> [--param <PARAM_FILE>] [--is_demo] [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `<CONFIG>` | Path to `pipeline.yaml` to be executed |\n| `--param <PARAM_FILE>` | Custom parameter file path, overrides default files under `parameter/` |\n| `--is_demo` | Enable demo mode |\n| `--log_level` | Log level, default `INFO` |\n\n---\n\n## show ui — Start Web Control Interface\n\n**Purpose**: Start UI interface.\n\n```bash\nultrarag show ui [--host 127.0.0.1] [--port 5050] [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `--host` | Web service binding IP (default `127.0.0.1`) |\n| `--port` | Web service port (default `5050`) |\n| `--log_level` | Log level, default `INFO` |\n\nAccess `http://<host>:<port>` after startup to use the UI.\n\n================\nFile: pages/en/api/corpus.mdx\n================\n---\ntitle: \"Corpus\"\nicon: \"file\"\n---\n\n\n## `build_text_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,text_corpus_save_path->None\")\nasync def build_text_corpus(parse_file_path: str, text_corpus_save_path: str) -> None\n```\n\n**Function**\n- Supports .txt / .md;\n- Supports .docx (reads paragraphs and tables);\n- Supports .pdf / .xps / .oxps / .epub / .mobi / .fb2 (pure text extraction via pymupdf).\n- Recursively processes in directory mode.\n\n**Output Format (JSONL)**\n```json\n{\"id\": \"<stem>\", \"title\": \"<stem>\", \"contents\": \"<full text>\"}\n```\n---\n\n## `build_image_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,image_corpus_save_path->None\")\nasync def build_image_corpus(parse_file_path: str, image_corpus_save_path: str) -> None\n```\n\n**Function**\n- **Only supports PDF**: Renders each page as JPG (RGB) at 144DPI and validates file validity.\n- Recursively processes in directory mode.\n\n**Output Index (JSONL)**\n```json\n{\"id\": 0, \"image_id\": \"paper/page_0.jpg\", \"image_path\": \"image/paper/page_0.jpg\"}\n```\n---\n\n## `mineru_parse`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,mineru_dir,mineru_extra_params->None\")\nasync def mineru_parse(\n    parse_file_path: str, \n    mineru_dir: str, \n    mineru_extra_params: Optional[Dict[str, Any]] = None\n) -> None\n```\n\n**Function**\n- Calls CLI `mineru` to structurally parse PDF/directory and outputs to `mineru_dir`.\n\n---\n\n## `build_mineru_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"mineru_dir,parse_file_path,text_corpus_save_path,image_corpus_save_path->None\")\nasync def build_mineru_corpus(\n    mineru_dir: str, \n    parse_file_path: str, \n    text_corpus_save_path: str, \n    image_corpus_save_path: str\n) -> None\n```\n\n**Function**\n- Aggregates MinerU parsing artifacts into **Text Corpus JSONL** and **Image Index JSONL**.\n\n\n**Output Format (JSONL)**\n- Text:\n```json\n{\"id\": \"<stem>\", \"title\": \"<stem>\", \"contents\": \"<markdown full text>\"}\n```\n- Image:\n```json\n{\"id\": 0, \"image_id\": \"paper/page_0.jpg\", \"image_path\": \"images/paper/page_0.jpg\"}\n```\n---\n\n## `chunk_documents`\n\n**Signature**\n```python\n@app.tool(output=\"raw_chunk_path,chunk_backend_configs,chunk_backend,tokenizer_or_token_counter,chunk_size,chunk_path,use_title->None\")\nasync def chunk_documents(\n    raw_chunk_path: str,\n    chunk_backend_configs: Dict[str, Any],\n    chunk_backend: str = \"token\",\n    tokenizer_or_token_counter: str = \"character\",\n    chunk_size: int = 256,\n    chunk_path: Optional[str] = None,\n    use_title: bool = True,\n) -> None\n```\n\n**Function**\n- Chunks input text corpus (JSONL, containing `id/title/contents`) into paragraphs using selected backend:\n- Chunk Backend: Supports `token` / `sentence` / `recursive`.\n- Tokenizer: `tokenizer_or_token_counter` can be `word`, `character`, or `tiktoken` encoding name (e.g., `gpt2`).\n- Chunk Size: Controls block size via `chunk_size` (overlap defaults to size/4).\n- Optionally appends document title to the beginning of each block (`use_title`).\n\n**Output Format (JSONL)**\n```json\n{\"id\": 0, \"doc_id\": \"paper\", \"title\": \"paper\", \"contents\": \"Chunked text\"}\n```\n\n---\n\n## Configuration\n\n```yaml servers/corpus/parameter.yaml icon=\"/images/yaml.svg\"\n# servers/corpus/parameter.yaml\nparse_file_path: data/UltraRAG.pdf\ntext_corpus_save_path: corpora/text.jsonl\nimage_corpus_save_path: corpora/image.jsonl\n\n# mineru\nmineru_dir: corpora/\nmineru_extra_params:\n  source: modelscope\n\n# chunking parameters\nraw_chunk_path: corpora/text.jsonl\nchunk_path: corpora/chunks.jsonl\nuse_title: false\nchunk_backend: sentence # choices=[\"token\", \"sentence\", \"recursive\"]\ntokenizer_or_token_counter: character\nchunk_size: 512\nchunk_backend_configs:\n  token:\n    chunk_overlap: 50\n  sentence:\n    chunk_overlap: 50\n    min_sentences_per_chunk: 1\n    delim: \"['.', '!', '?', '；', '。', '！', '？', '\\\\n']\"\n  recursive:\n    min_characters_per_chunk: 12\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `parse_file_path` | str | Input file or directory path |\n| `text_corpus_save_path` | str | Text corpus output path (JSONL) |\n| `image_corpus_save_path` | str | Image corpus index output path (JSONL) |\n| `mineru_dir` | str | MinerU output root directory |\n| `mineru_extra_params` | dict | MinerU extra parameters, such as `source`, `layout`, etc. |\n| `raw_chunk_path` | str | Chunking input file path (JSONL format) |\n| `chunk_path` | str | Chunking output path |\n| `use_title` | bool | Whether to append document title to the beginning of each chunk |\n| `chunk_backend` | str | Select chunking method: `token`, `sentence`, `recursive` |\n| `tokenizer_or_token_counter` | str | Tokenizer or counting method. Options: `word`, `character` or `tiktoken` model name (e.g., `gpt2`) |\n| `chunk_size` | int | Target size for each chunk |\n| `chunk_backend_configs` | dict | Configuration items for each chunking method (see below) |\n\n`chunk_backend_configs` Detailed Parameters:\n\n| Backend Type | Parameter | Description |\n|---|---|---|\n| **token** | `chunk_overlap` | Overlapping tokens between chunks |\n| **sentence** | `chunk_overlap` | Overlapping count between chunks |\n|  | `min_sentences_per_chunk` | Minimum number of sentences per chunk |\n|  | `delim` | Sentence delimiter list (Python list in string format) |\n| **recursive** | `min_characters_per_chunk` | Minimum character unit for recursive splitting |\n\n================\nFile: pages/en/api/custom.mdx\n================\n---\ntitle: \"Custom\"\nicon: \"puzzle-piece\"\n---\n\n## Search-R1 Tools\n\n### `search_r1_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef search_r1_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts query content from model response.\n- **Logic**: Uses regex `r\"<search>([^<]*)\"` to extract the content inside the last `<search>` tag. If not found, returns \"There is no query.\"; if the query does not end with `?`, it is automatically completed.\n\n### `r1_searcher_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef r1_searcher_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts query from R1-Searcher response.\n- **Logic**: Uses regex `r\"<|begin_of_query|>([^<]*)\"` to extract the last tag content.\n\n---\n\n## IRCoT & IterRetGen Tools\n\n### `iterretgen_nextquery`\n```python\n@app.tool(output=\"q_ls,ret_psg->nextq_ls\")\ndef iterretgen_nextquery(q_ls: List[str], ans_ls: List[str | Any]) -> Dict[str, List[str]]\n```\n- **Function**: Iterative retrieval generation.\n- **Logic**: `next_query = f\"{q} {ans}\"`. Concatenates original question and generated answer as the Query for the next retrieval.\n\n### `ircot_get_first_sent`\n```python\n@app.tool(output=\"ans_ls->q_ls\")\ndef ircot_get_first_sent(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts the first sentence of the answer (up to period or question/exclamation mark).\n\n### `ircot_extract_ans`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef ircot_extract_ans(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts the final answer.\n- **Logic**: Matches content after `so the answer is [...]`.\n\n---\n\n## Search-o1 Tools\n\n### `search_o1_init_list`\n```python\n@app.tool(output=\"q_ls->total_subq_list,total_reason_list,total_final_info_list\")\ndef search_o1_init_list(q_ls: List[str]) -> Dict[str, List[Any]]\n```\n- **Function**: Initializes accumulation lists required by Search-o1 (sub-questions, reasoning, final info), initially filled with `<PAD>`.\n\n### `search_o1_combine_list`\n```python\n@app.tool(output=\"total_subq_list, extract_query_list, total_reason_list, extract_reason_list->total_subq_list, total_reason_list\")\ndef search_o1_combine_list(...)\n```\n- **Function**: Appends the extracted Query and Reasoning of the current step to the total lists.\n\n### `search_o1_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef search_o1_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts content between `<|begin_search_query|>...<|end_search_query|>`.\n\n### `search_o1_reasoning_extract`\n```python\n@app.tool(output=\"ans_ls->extract_reason_list\")\ndef search_o1_reasoning_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts all text before `<|begin_search_query|>` as the reasoning process.\n\n### `search_o1_extract_final_information`\n```python\n@app.tool(output=\"ans_ls->extract_final_infor_list\")\ndef search_o1_extract_final_information(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts content after `**Final Information**` marker.\n\n---\n\n## Utility Tools\n\n### `output_extract_from_boxed`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef output_extract_from_boxed(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts answer from LaTeX `\\boxed{...}`. Supports nested bracket handling and format cleaning.\n\n### `merge_passages`\n```python\n@app.tool(output=\"temp_psg,ret_psg->ret_psg\")\ndef merge_passages(temp_psg: List[str | Any], ret_psg: List[str | Any]) -> Dict[str, List[str | Any]]\n```\n- **Function**: Appends `temp_psg` list to `ret_psg` list.\n\n### `evisrag_output_extract_from_special`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef evisrag_output_extract_from_special(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts answer from `<answer>...</answer>` tags.\n\n### `assign_citation_ids` / `assign_citation_ids_stateful`\n- `assign_citation_ids`: Assigns citation IDs in the form of `[1]`, `[2]` to retrieved passages.\n- `assign_citation_ids_stateful`: Uses `CitationRegistry` class to maintain global citation IDs (cross-step deduplication).\n- `init_citation_registry`: Resets global citation registry.\n\n---\n\n## SurveyCPM Tools\n\n### `surveycpm_state_init`\n```python\n@app.tool(output=\"instruction_ls->state_ls,cursor_ls,survey_ls,step_ls,extend_time_ls,extend_result_ls,retrieved_info_ls,parsed_ls\")\ndef surveycpm_state_init(instruction_ls: List[str]) -> Dict[str, List]\n```\n- **Function**: Initializes SurveyCPM state machine.\n- **Initial State**: `state=\"search\"`, `cursor=\"outline\"`, `step=0`.\n\n### `surveycpm_parse_search_response`\n```python\n@app.tool(output=\"response_ls,surveycpm_hard_mode->keywords_ls,parsed_ls\")\ndef surveycpm_parse_search_response(response_ls: List[str], surveycpm_hard_mode: bool = True) -> Dict[str, List]\n```\n- **Function**: Parses search instructions (JSON or XML format) generated by the model, extracts keyword list.\n\n### `surveycpm_process_passages`\n```python\n@app.tool(output=\"ret_psg_ls->retrieved_info_ls\")\ndef surveycpm_process_passages(ret_psg_ls: List[List[List[str]]]) -> Dict[str, List[str]]\n```\n- **Function**: Processes retrieved passages, deduplicates, limits quantity (Top-K), and concatenates into string.\n\n### `surveycpm_after_init_plan` / `after_write` / `after_extend`\n- **Function**: Parses Agent response for different stages (initialize outline, write content, extend plan).\n- **Logic**:\n  - Calls `surveycpm_parse_response` to validate format and content.\n  - Updates `survey_ls` (outline structure) and `cursor_ls` (current cursor position) if successful.\n  - Keeps original state for retry if failed.\n\n### `surveycpm_update_state`\n```python\n@app.tool(output=\"state_ls,cursor_ls,extend_time_ls,extend_result_ls,step_ls,parsed_ls,surveycpm_max_step,surveycpm_max_extend_step->state_ls,extend_time_ls,step_ls\")\ndef surveycpm_update_state(...)\n```\n- **Function**: Core state machine logic.\n- **State Transition**:\n  - `search` -> `analyst-init_plan` (cursor=\"outline\")\n  - `search` -> `write` (cursor=section-X)\n  - `write` -> `search` (continue writing) or `analyst-extend_plan` (finished current section)\n  - `analyst-extend_plan` -> `search` (extend success) or `done` (no extension)\n  - Exceeds max steps -> `done`\n\n### `surveycpm_format_output`\n```python\n@app.tool(output=\"survey_ls,instruction_ls->ans_ls\")\ndef surveycpm_format_output(survey_ls: List[str], instruction_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Converts final Survey JSON to Markdown format.\n- **Processing**: Automatically handles heading levels (# ## ###), citation formatting (`\\cite{...}` to `[1]`), and text cleaning.\n\n---\n\n## Configuration\n\n```yaml servers/custom/parameter.yaml icon=\"/images/yaml.svg\"\nsurveycpm_hard_mode: false\nsurveycpm_max_step: 140\nsurveycpm_max_extend_step: 12\n```\n\n| Parameter | Type | Description |\n|---|---|---|\n| `surveycpm_hard_mode` | bool | Whether to enable SurveyCPM's strict parsing mode (validate JSON field integrity) |\n| `surveycpm_max_step` | int | Maximum total execution steps, forced end if exceeded |\n| `surveycpm_max_extend_step` | int | Maximum plan extension times |\n\n================\nFile: pages/en/api/evaluation.mdx\n================\n---\ntitle: \"Evaluation\"\nicon: \"clipboard-check\"\n---\n\n## `evaluate`\n\n**Signature**\n```python\n@app.tool(output=\"pred_ls,gt_ls,metrics,save_path->eval_res\")\ndef evaluate(\n    pred_ls: List[str],\n    gt_ls: List[List[str]],\n    metrics: List[str] | None,\n    save_path: str,\n) -> Dict[str, Any]\n```\n\n**Function**\n- Executes automatic metric evaluation for QA/Generation tasks.\n- Supported Metrics: `acc`, `em`, `coverem`, `stringem`, `f1`, `rouge-1`, `rouge-2`, `rouge-l`.\n- Results are automatically saved as `.json` file and printed as Markdown table.\n\n\n---\n\n## `evaluate_trec`\n\n**Signature**\n```python\n@app.tool(output=\"run_path,qrels_path,ir_metrics,ks,save_path->eval_res\")\ndef evaluate_trec(\n    run_path: str,\n    qrels_path: str,\n    metrics: List[str] | None,\n    ks: List[int] | None,\n    save_path: str,\n)\n```\n\n**Function**\n- Performs IR retrieval metric evaluation based on `pytrec_eval`.\n- Reads standard TREC format:\n  - **qrels**: `<qid> <iter> <docid> <rel>`\n  - **run**: `<qid> Q0 <docid> <rank> <score> <tag>`\n- Supported Metrics: `mrr`, `map`, `recall@k`, `precision@k`, `ndcg@k`.\n- Automatically aggregates statistics and outputs as table.\n\n---\n\n## `evaluate_trec_pvalue`\n\n**Signature**\n```python\n@app.tool(\n    output=\"run_new_path,run_old_path,qrels_path,ir_metrics,ks,n_resamples,save_path->eval_res\"\n)\ndef evaluate_trec_pvalue(\n    run_new_path: str,\n    run_old_path: str,\n    qrels_path: str,\n    metrics: List[str] | None,\n    ks: List[int] | None,\n    n_resamples: int | None,\n    save_path: str,\n)\n```\n\n**Function**\n- Compares significance of two TREC result files using **Two-sided Permutation Test** to calculate p-value.\n- Default resampling count `n_resamples=10000`.\n- Outputs mean, difference, p-value, and significance flag.\n\n\n---\n\n## Configuration\n\n```yaml servers/evaluation/parameter.yaml icon=\"/images/yaml.svg\"\nsave_path: output/evaluate_results.json\n\n# QA task\nmetrics: [ 'acc', 'f1', 'em', 'coverem', 'stringem', 'rouge-1', 'rouge-2', 'rouge-l' ]\n\n# Retrieval task\nqrels_path: data/qrels.txt\nrun_path: data/run_a.txt\nks: [ 1, 5, 10, 20, 50, 100 ]\nir_metrics: [ \"mrr\", \"map\", \"recall\", \"ndcg\", \"precision\" ]\n\n# significant\nrun_new_path: data/run_a.txt\nrun_old_path: data/run_b.txt\nn_resamples: 10000\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `save_path` | str | Evaluation result save path (automatically appends timestamp) |\n| `metrics` | list[str] | Metric set used for QA / Generation tasks |\n| `qrels_path` | str | TREC format ground truth file path |\n| `run_path` | str | Result file for retrieval task |\n| `ks` | list[int] | Truncation levels for calculating NDCG@K, P@K, Recall@K, etc. |\n| `ir_metrics` | list[str] | Retrieval task metric names, supports `mrr`, `map`, `recall`, `ndcg`, `precision` |\n| `run_new_path` | str | Run file path generated by new model (significance analysis) |\n| `run_old_path` | str | Run file path of old model (significance analysis) |\n| `n_resamples` | int | Resampling count for Permutation Test |\n\n================\nFile: pages/en/api/generation.mdx\n================\n---\ntitle: \"Generation\"\nicon: \"pen-nib\"\n---\n\n## `generation_init`\n\n**Signature**\n```python\ndef generation_init(\n    backend_configs: Dict[str, Any],\n    sampling_params: Dict[str, Any],\n    extra_params: Optional[Dict[str, Any]] = None,\n    backend: str = \"vllm\",\n) -> None\n```\n\n**Function**\n- Initializes inference backend and sampling parameters.\n- Supports `vllm`, `openai`, `hf` backends.\n- `extra_params` can be used to pass `chat_template_kwargs` or other backend-specific parameters.\n\n---\n\n## `generate`\n\n**Signature**\n```python\nasync def generate(\n    prompt_ls: List[Union[str, Dict[str, Any]]],\n    system_prompt: str = \"\",\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Plain text conversation generation.\n- Automatically handles Prompt in list, supports string or OpenAI format dictionary.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"answer for prompt_0\", \"answer for prompt_1\", \"...\"]}\n```\n\n---\n\n## `multimodal_generate`\n\n**Signature**\n```python\nasync def multimodal_generate(\n    multimodal_path: List[List[str]],\n    prompt_ls: List[Union[str, Dict[str, Any]]],\n    system_prompt: str = \"\",\n    image_tag: Optional[str] = None,\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Text-image multimodal conversation generation.\n- `multimodal_path`: List of image paths corresponding to each Prompt (supports local path or URL).\n- `image_tag`: If specified (e.g., `<img>`), inserts image at that tag's position in Prompt; otherwise defaults to appending to end of Prompt.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"answer with images for prompt_0\", \"...\"]}\n```\n\n---\n\n## `multiturn_generate`\n\n**Signature**\n```python\nasync def multiturn_generate(\n    messages: List[Dict[str, str]],\n    system_prompt: str = \"\",\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Multi-turn conversation generation.\n- Supports only single-call generation, does not handle batch Prompts.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"assistant response\"]}\n```\n\n---\n\n## `vllm_shutdown`\n\n**Signature**\n```python\ndef vllm_shutdown() -> None\n```\n\n**Function**\n- Explicitly shuts down vLLM engine and releases VRAM resources.\n- Valid only when using `vllm` backend.\n\n---\n\n## Configuration\n\n```yaml servers/generation/parameter.yaml icon=\"/images/yaml.svg\"\n# servers/generation/parameter.yaml\nbackend: vllm # options: vllm, openai\nbackend_configs:\n  vllm:\n    model_name_or_path: openbmb/MiniCPM4-8B\n    gpu_ids: \"2,3\"\n    gpu_memory_utilization: 0.9\n    dtype: auto\n    trust_remote_code: true\n  openai:\n    model_name: MiniCPM4-8B\n    base_url: http://localhost:8000/v1\n    api_key: \"abc\"\n    concurrency: 8\n    retries: 3\n    base_delay: 1.0\n  hf:\n    model_name_or_path: openbmb/MiniCPM4-8B\n    gpu_ids: '2,3'\n    trust_remote_code: true\n    batch_size: 8\nsampling_params:\n  temperature: 0.7\n  top_p: 0.8\n  max_tokens: 2048\nextra_params:\n  chat_template_kwargs:\n    enable_thinking: false\nsystem_prompt: \"\"\nimage_tag: null\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `backend` | str | Specify generation backend, options `vllm`, `openai`, or `hf` (Transformers) |\n| `backend_configs` | dict | Model and runtime environment configuration for each backend |\n| `sampling_params` | dict | Sampling parameters to control generation diversity and length |\n| `extra_params` | dict | Extra parameters, e.g., `chat_template_kwargs` |\n| `system_prompt` | str | Global system prompt, added to context as `system` message |\n| `image_tag` | str | Image placeholder tag (if needed) |\n\n`backend_configs` Detailed Description:\n\n| Backend | Parameter | Description |\n|---|---|---|\n| **vllm** | `model_name_or_path` | Model name or path |\n|  | `gpu_ids` | GPU IDs used (e.g., `\"0,1\"`) |\n|  | `gpu_memory_utilization` | GPU memory utilization ratio (0–1) |\n|  | `dtype` | Data type (e.g., `auto`, `bfloat16`) |\n|  | `trust_remote_code` | Whether to trust remote code |\n| **openai** | `model_name` | OpenAI model name or self-hosted compatible model |\n|  | `base_url` | API base URL |\n|  | `api_key` | API Key |\n|  | `concurrency` | Max concurrent requests |\n|  | `retries` | API retry count |\n|  | `base_delay` | Base wait time for each retry (seconds) |\n| **hf** | `model_name_or_path` | Transformers model path |\n|  | `gpu_ids` | GPU IDs (same as above) |\n|  | `trust_remote_code` | Whether to trust remote code |\n|  | `batch_size` | Batch size per inference |\n\n`sampling_params` Detailed Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `temperature` | float | Controls randomness, higher means more diverse generation |\n| `top_p` | float | Nucleus sampling threshold |\n| `max_tokens` | int | Max generated tokens |\n\n================\nFile: pages/en/api/prompt.mdx\n================\n---\ntitle: \"Prompt\"\nicon: \"terminal\"\n---\n\n## QA Prompts\n\n### `qa_boxed`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,template->prompt_ls\")\ndef qa_boxed(\n    q_ls: List[str], \n    template: str | Path\n) -> List[PromptMessage]\n```\n\n**Function**\n\nBasic Q&A Prompt.\n\nLoads specified Jinja2 template, renders each question in the question list into a Prompt.\n\nTemplate Variable: `{{ question }}`\n\n### `qa_boxed_multiple_choice`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,choices_ls,template->prompt_ls\")\ndef qa_boxed_multiple_choice(\n    q_ls: List[str],\n    choices_ls: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nMultiple Choice Q&A Prompt.\n\nAutomatically formats choice list into \"A: ..., B: ...\" form and injects into template.\n\nTemplate Variables: `{{ question }}`, `{{ choices }}`\n\n### `qa_rag_boxed`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,template->prompt_ls\")\ndef qa_rag_boxed(\n    q_ls: List[str], \n    ret_psg: List[str | Any], \n    template: str | Path\n) -> list[PromptMessage]\n```\n\n**Function**\n\nStandard RAG Prompt.\n\nConcatenates retrieved passage lists and injects into template.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `qa_rag_boxed_multiple_choice`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,choices_ls,ret_psg,template->prompt_ls\")\ndef qa_rag_boxed_multiple_choice(\n    q_ls: List[str],\n    choices_ls: List[List[str]],\n    ret_psg: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nMultiple Choice Q&A Prompt with Retrieval Context.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`, `{{ choices }}`\n\n---\n\n## RankCoT Prompts\n\n### `RankCoT_kr`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,kr_template->prompt_ls\")\ndef RankCoT_kr(\n    q_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> list[PromptMessage]\n```\n\n**Function**\n\nRankCoT Phase 1: Knowledge Retrieval Prompt.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `RankCoT_qa`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,kr_ls,qa_template->prompt_ls\")\ndef RankCoT_qa(\n    q_ls: List[str],\n    kr_ls: List[str],\n    template: str | Path,\n) -> list[PromptMessage]\n```\n\n**Function**\n\nRankCoT Phase 2: Chain-of-Thought based Q&A Prompt.\n\nTemplate Variables: `{{ question }}`, `{{ CoT }}` (Here CoT is usually knowledge generated in the previous phase)\n\n---\n\n## IRCoT Prompts\n\n### `ircot_next_prompt`\n\n**Signature**\n\n```python\n@app.prompt(output=\"memory_q_ls,memory_ret_psg,template->prompt_ls\")\ndef ircot_next_prompt(\n    memory_q_ls: List[List[str | None]],\n    memory_ret_psg: List[List[List[str]] | None],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nIRCoT (Interleaved Retrieval CoT) Iterative Prompt Generation.\n\nConstructs next round's Prompt based on historical retrieval results and chain of thought. Supports single-turn and multi-turn history concatenation.\n\nTemplate Variables: `{{ documents }}`, `{{ question }}`, `{{ cur_answer }}`\n\n---\n\n## WebNote Prompts\n\n### `webnote_init_page`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,webnote_init_page_template->prompt_ls\")\ndef webnote_init_page(\n    q_ls: List[str],\n    plan_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Initialize note page.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`\n\n### `webnote_gen_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,webnote_gen_plan_template->prompt_ls\")\ndef webnote_gen_plan(\n    q_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate search plan.\n\nTemplate Variable: `{{ question }}`\n\n### `webnote_gen_subq`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,page_ls,webnote_gen_subq_template->prompt_ls\")\ndef webnote_gen_subq(\n    q_ls: List[str],\n    plan_ls: List[str],\n    page_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate sub-questions.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`, `{{ page }}`\n\n### `webnote_fill_page`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,page_ls,subq_ls,psg_ls,webnote_fill_page_template->prompt_ls\")\ndef webnote_fill_page(\n    q_ls: List[str],\n    plan_ls: List[str],\n    page_ls: List[str],\n    subq_ls: List[str],\n    psg_ls: List[Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Fill notes based on retrieval results.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`, `{{ sub_question }}`, `{{ docs_text }}`, `{{ page }}`\n\n### `webnote_gen_answer`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,page_ls,webnote_gen_answer_template->prompt_ls\")\ndef webnote_gen_answer(\n    q_ls: List[str],\n    page_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate final answer based on notes.\n\nTemplate Variables: `{{ question }}`, `{{ page }}`\n\n---\n\n## Search-R1 & R1-Searcher\n\n### `search_r1_gen`\n\n**Signature**\n\n```python\n@app.prompt(output=\"prompt_ls,ans_ls,ret_psg,search_r1_gen_template->prompt_ls\")\ndef search_r1_gen(\n    prompt_ls: List[PromptMessage],\n    ans_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nGeneration Prompt suitable for R1 style.\n\nTruncates Top-3 retrieval passages and injects into context.\n\nTemplate Variables: `{{ history }}`, `{{ answer }}`, `{{ passages }}`\n\n### `r1_searcher_gen`\n\n**Signature**\n\n```python\n@app.prompt(output=\"prompt_ls,ans_ls,ret_psg,r1_searcher_gen_template->prompt_ls\")\ndef r1_searcher_gen(\n    prompt_ls: List[PromptMessage],\n    ans_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nGeneration Prompt suitable for R1 Searcher.\n\nTruncates Top-5 retrieval passages.\n\nTemplate Variables: `{{ history }}`, `{{ answer }}`, `{{ passages }}`\n\n---\n\n## Search-o1 Prompts\n\n### `search_o1_init`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,searcho1_reasoning_template->prompt_ls\")\ndef search_o1_init(\n    q_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Initial Reasoning Prompt.\n\nTemplate Variable: `{{ question }}`\n\n### `search_o1_reasoning_indocument`\n\n**Signature**\n\n```python\n@app.prompt(output=\"extract_query_list,ret_psg,total_reason_list,searcho1_refine_template->prompt_ls\")\ndef search_o1_reasoning_indocument(\n    extract_query_list: List[str], \n    ret_psg: List[List[str]],       \n    total_reason_list: List[List[str]], \n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Reasoning Refinement Prompt.\n\nMerges historical reasoning steps (first + last 3 steps) with current retrieved documents for next step reasoning.\n\nTemplate Variables: `{{ prev_reasoning }}`, `{{ search_query }}`, `{{ document }}`\n\n### `search_o1_insert`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,total_subq_list,total_final_info_list,searcho1_reasoning_template->prompt_ls\") \ndef search_o1_insert(\n    q_ls: List[str],\n    total_subq_list: List[List[str]], \n    total_final_info_list: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Formatted Insertion Prompt.\n\nExplicitly inserts `<|begin_search_query|>` and search result tags into Prompt to construct complete chain-of-thought context.\n\n---\n\n## EVisRAG & Multi-branch Prompts\n\n### `gen_subq`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,gen_subq_template->prompt_ls\")\ndef gen_subq(\n    q_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nLoop/Branch Demo: Generate sub-questions based on documents.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `evisrag_vqa`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,evisrag_template->prompt_ls\")\ndef evisrag_vqa(\n    q_ls: List[str], \n    ret_psg: List[str | Any], \n    template: str | Path\n) -> list[PromptMessage]\n```\n\n**Function**\n\nMultimodal VQA RAG Prompt.\n\nAutomatically repeatedly inserts `<image>` Token into Prompt according to the number of retrieved images.\n\nTemplate Variable: `{{ question }}` (contains automatically injected image tokens)\n\n---\n\n## SurveyCPM Prompts\n\n### `surveycpm_search`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,cursor_ls,surveycpm_search_template->prompt_ls\")\ndef surveycpm_search(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    cursor_ls: List[str | None],\n    surveycpm_search_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Decide next search content.\n\nParses JSON format outline, generates text description of current outline.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_outline }}`, `{{ current_instruction }}`\n\n### `surveycpm_init_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,retrieved_info_ls,surveycpm_init_plan_template->prompt_ls\")\ndef surveycpm_init_plan(\n    instruction_ls: List[str],\n    retrieved_info_ls: List[str],\n    surveycpm_init_plan_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Initialize outline plan.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_information }}`\n\n### `surveycpm_write`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,cursor_ls,retrieved_info_ls,surveycpm_write_template->prompt_ls\")\ndef surveycpm_write(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    cursor_ls: List[str | None],\n    retrieved_info_ls: List[str],\n    surveycpm_write_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Write specific section content.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_survey }}`, `{{ current_instruction }}`, `{{ current_information }}`\n\n### `surveycpm_extend_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,surveycpm_extend_plan_template->prompt_ls\")\ndef surveycpm_extend_plan(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    surveycpm_extend_plan_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Extend or modify outline plan.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_survey }}`\n\n---\n\n## Configuration\n\n```yaml servers/prompt/parameter.yaml icon=\"/images/yaml.svg\"\n# QA\ntemplate: prompt/qa_boxed.jinja\n\n# RankCoT\nkr_template: prompt/RankCoT_knowledge_refinement.jinja\nqa_template: prompt/RankCoT_question_answering.jinja\n\n# Search-R1\nsearch_r1_gen_template: prompt/search_r1_append.jinja\n\n# R1-Searcher\nr1_searcher_gen_template: prompt/r1_searcher_append.jinja\n\n# Search-o1\nsearcho1_reasoning_template: prompt/search_o1_reasoning.jinja\nsearcho1_refine_template: prompt/search_o1_refinement.jinja\n\n\n# For other prompts, please add parameters here as needed\n\n# Take webnote as an example:\nwebnote_gen_plan_template: prompt/webnote_gen_plan.jinja\nwebnote_init_page_template: prompt/webnote_init_page.jinja\nwebnote_gen_subq_template: prompt/webnote_gen_subq.jinja\nwebnote_fill_page_template: prompt/webnote_fill_page.jinja\nwebnote_gen_answer_template: prompt/webnote_gen_answer.jinja\n\n# SurveyCPM\nsurveycpm_search_template: prompt/surveycpm_search.jinja\nsurveycpm_init_plan_template: prompt/surveycpm_init_plan.jinja\nsurveycpm_write_template: prompt/surveycpm_write.jinja\nsurveycpm_extend_plan_template: prompt/surveycpm_extend_plan.jinja\n```\n\n| Parameter | Description |\n|---|---|\n| `template` | Basic QA template path |\n| `kr_template` | RankCoT knowledge refinement template path |\n| `qa_template` | RankCoT Q&A template path |\n| `*_template` | Jinja2 template file path corresponding to each module function |\n\n================\nFile: pages/en/api/reranker.mdx\n================\n---\ntitle: \"Reranker\"\nicon: \"ranking-star\"\n---\n\n## `reranker_init`\n\n**Signature**\n```python\nasync def reranker_init(\n    model_name_or_path: str,\n    backend_configs: Dict[str, Any],\n    batch_size: int,\n    gpu_ids: Optional[object] = None,\n    backend: str = \"infinity\",\n) -> None\n```\n\n**Function**\n- Initializes reranker backend and model.\n\n---\n\n## `reranker_rerank`\n\n**Signature**\n```python\nasync def reranker_rerank(\n    query_list: List[str],\n    passages_list: List[List[str]],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n) -> Dict[str, List[Any]]\n```\n\n**Function**\n- **Reranks** candidate passages:\n\n**Output Format (JSON)**\n```json\n{\n  \"rerank_psg\": [\n    [\"best passage for q0\", \"...\"],\n    [\"best passage for q1\", \"...\"]\n  ]\n}\n```\n\n---\n\n## Configuration\n\n```yaml servers/reranker/parameter.yaml icon=\"/images/yaml.svg\"\nmodel_name_or_path: openbmb/MiniCPM-Reranker-Light\nbackend: sentence_transformers # options: infinity, sentence_transformers, openai\nbackend_configs:\n  infinity:\n    bettertransformer: false\n    pooling_method: auto\n    device: cuda\n    model_warmup: false\n    trust_remote_code: true\n  sentence_transformers:\n    device: cuda\n    trust_remote_code: true\n  openai:\n    model_name: text-embedding-3-small\n    base_url: \"https://api.openai.com/v1\"\n    api_key: \"\"\n\ngpu_ids: 0\ntop_k: 5\nbatch_size: 16\nquery_instruction: \"\"\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `model_name_or_path` | str | Model path or name (local or HuggingFace repo) |\n| `backend` | str | Select backend type: `infinity`, `sentence_transformers` or `openai` |\n| `backend_configs` | dict | Exclusive parameter settings for each backend |\n| `gpu_ids` | str/int | Specify GPU ID (can be multi-card, e.g., `\"0,1\"`) |\n| `top_k` | int | Number of reranked results returned |\n| `batch_size` | int | Sample quantity per batch |\n| `query_instruction` | str | Query prefix hint, used for prompt engineering or query modification |\n\n`backend_configs` Detailed Description:\n\n| Backend | Parameter | Description |\n|---|---|---|\n| **infinity** | `device` | Device type (cuda / cpu) |\n|  | `bettertransformer` | Whether to enable accelerated inference |\n|  | `pooling_method` | Vector pooling strategy |\n|  | `model_warmup` | Whether to warmup model |\n|  | `trust_remote_code` | Whether to trust remote code (Required for HuggingFace models) |\n| **sentence_transformers** | `device` | Device type (cuda / cpu) |\n|  | `trust_remote_code` | Whether to trust remote code |\n| **openai** | `model_name` | API Model name |\n|  | `base_url` | API access address |\n|  | `api_key` | OpenAI API Key |\n\n================\nFile: pages/en/api/retriever.mdx\n================\n---\ntitle: \"Retriever\"\nicon: \"magnifying-glass\"\n---\n\n## `retriever_init`\n\n**Signature**\n```python\nasync def retriever_init(\n    model_name_or_path: str,\n    backend_configs: Dict[str, Any],\n    batch_size: int,\n    corpus_path: str,\n    gpu_ids: Optional[object] = None,\n    is_multimodal: bool = False,\n    backend: str = \"sentence_transformers\",\n    index_backend: str = \"faiss\",\n    index_backend_configs: Optional[Dict[str, Any]] = None,\n    is_demo: bool = False,\n    collection_name: str = \"\",\n) -> None\n```\n\n**Function**\n- Initializes retrieval service.\n- Embedding Backend (backend): Responsible for converting text/images into vectors (Infinity, SentenceTransformers, OpenAI, BM25).\n- Index Backend (index_backend): Responsible for vector storage and retrieval (FAISS, Milvus, Qdrant).\n- Demo Mode: If is_demo=True, forces OpenAI + Milvus configuration, ignoring some parameters.\n\n---\n\n## `retriever_embed`\n\n**Signature**\n```python\nasync def retriever_embed(\n    embedding_path: Optional[str] = None,\n    overwrite: bool = False,\n    is_multimodal: bool = False,\n) -> None\n```\n\n**Function**\n- (Non-Demo Mode) Batches calculation of vector representations of corpus and saves as .npy file.\n- Only applies to Dense Retriever backend (BM25 not supported).\n\n\n---\n\n## `retriever_index`\n\n**Signature**\n```python\nasync def retriever_index(\n    embedding_path: str,\n    overwrite: bool = False,\n    collection_name: str = \"\",\n    corpus_path: str = \"\"\n) -> None\n```\n\n**Function**\n- Builds retrieval index.\n- FAISS: Reads embedding_path (.npy) to build local index file.\n- Milvus / Qdrant / Demo: Reads corpus_path (.jsonl), generates vectors and inserts into specified collection_name.\n\n---\n\n## `retriever_search`\n\n**Signature**\n```python\nasync def retriever_search(\n    query_list: List[str],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n    collection_name: str = \"\",\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Retrieves single or multiple queries.\n- Automatically handles query vectorization (adds query_instruction) and finds Top-K in specified collection_name (for Milvus or Qdrant) or default index.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_batch_search`\n\n**Signature**\n```python\nasync def retriever_batch_search(\n    batch_query_list: List[List[str]],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n    collection_name: str = \"\",\n) -> Dict[str, List[List[List[str]]]]\n```\n\n**Function**\n- Batch version of retriever_search, accepts nested list input.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg_ls\": [[[\"psg 1-1\"], [\"psg 1-2\"]], [[\"psg 2-1\"]]]}\n```\n\n---\n\n## `bm25_index`\n\n**Signature**\n```python\nasync def bm25_index(\n    overwrite: bool = False,\n) -> None\n```\n\n**Function**\n- When `backend=\"bm25\"`, builds BM25 sparse index and saves it.\n\n---\n\n## `bm25_search`\n\n**Signature**\n```python\nasync def bm25_search(\n    query_list: List[str],\n    top_k: int = 5,\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Keyword retrieval based on BM25 algorithm.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_deploy_search`\n\n**Signature**\n```python\nasync def retriever_deploy_search(\n    retriever_url: str,\n    query_list: List[str],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- As a client, calls remote retrieval service deployed at retriever_url for query.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_websearch`\n\n**Signature**\n```python\nasync def retriever_websearch(\n    query_list: List[str],\n    top_k: Optional[int] | None = 5,\n    retrieve_thread_num: Optional[int] | None = 1,\n    websearch_backend: str = \"tavily\",\n    websearch_backend_configs: Optional[Dict[str, Any]] | None = None,\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Unified Web retrieval tool. Choose backend via `websearch_backend` (`tavily`, `exa`, `zhipuai`) and configure in `websearch_backend_configs`.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"snippet 1\", \"snippet 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_batch_websearch`\n\n**Signature**\n```python\nasync def retriever_batch_websearch(\n    batch_query_list: List[List[str]],\n    top_k: Optional[int] | None = 5,\n    retrieve_thread_num: Optional[int] | None = 1,\n    websearch_backend: str = \"tavily\",\n    websearch_backend_configs: Optional[Dict[str, Any]] | None = None,\n) -> Dict[str, List[List[List[str]]]]\n```\n\n**Function**\n- Batch Web retrieval for SurveyCPM-style pipelines.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg_ls\": [[[\"snippet 1\", \"snippet 2\"]], [\"...\" ]]} \n```\n\n\n---\n\n## Configuration\n\n```yaml servers/retriever/parameter.yaml icon=\"/images/yaml.svg\"\nmodel_name_or_path: openbmb/MiniCPM-Embedding-Light\ncorpus_path: data/corpus_example.jsonl\nembedding_path: embedding/embedding.npy\ncollection_name: wiki\n\n# Embedding Backend Configuration\nbackend: sentence_transformers # options: infinity, sentence_transformers, openai, bm25\nbackend_configs:\n  infinity:\n    bettertransformer: false\n    pooling_method: auto\n    model_warmup: false\n    trust_remote_code: true\n  sentence_transformers:\n    trust_remote_code: true\n    sentence_transformers_encode:\n      normalize_embeddings: false\n      encode_chunk_size: 256\n      q_prompt_name: query\n      psg_prompt_name: document\n      psg_task: null\n      q_task: null\n  openai:\n    model_name: text-embedding-3-small\n    base_url: \"https://api.openai.com/v1\"\n    api_key: \"abc\"\n  bm25:\n    lang: en\n    save_path: index/bm25\n\n# Index Backend Configuration\nindex_backend: faiss # options: faiss, milvus, qdrant\nindex_backend_configs:\n  faiss:\n    index_use_gpu: True\n    index_chunk_size: 10000\n    index_path: index/index.index\n  milvus:\n    uri: index/milvus_demo.db # Local file for Lite, or http://host:port\n    token: null\n    id_field_name: id\n    vector_field_name: vector\n    text_field_name: contents\n    index_params:\n      index_type: AUTOINDEX\n      metric_type: IP\n\n# Websearch Backend Configuration\nwebsearch_backend: tavily # options: tavily, exa, zhipuai\nwebsearch_backend_configs:\n  exa:\n    api_key: \"\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n  tavily:\n    api_key: \"\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n  zhipuai:\n    api_key: \"\"\n    base_url: \"https://open.bigmodel.cn/api/paas/v4/web_search\"\n    search_engine: \"search_std\"\n    search_intent: false\n    search_recency_filter: \"noLimit\"\n    content_size: \"medium\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n\nbatch_size: 16\ntop_k: 5\ngpu_ids: \"1\"\nquery_instruction: \"\"\nis_multimodal: false\noverwrite: false\nretrieve_thread_num: 1\nretriever_url: \"http://127.0.0.1:64501\"\nis_demo: false\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `model_name_or_path` | str | Retrieval model path or name (e.g., HuggingFace model ID) |\n| `corpus_path` | str | Input corpus JSONL file path |\n| `embedding_path` | str | Vector file save path (`.npy`) |\n| `collection_name` | str | Milvus or Qdrant collection name |\n| `backend` | str | Select retrieval backend: `infinity`, `sentence_transformers`, `openai`, `bm25` |\n| `index_backend` | str | Index backend: `faiss`, `milvus`, `qdrant` |\n| `backend_configs` | dict | Parameter configuration for each backend (see table below) |\n| `index_backend_configs` | dict | Parameter configuration for each index backend (see table below) |\n| `websearch_backend` | str | Web search backend: `tavily`, `exa`, `zhipuai` |\n| `websearch_backend_configs` | dict | Web search backend configs (api_key, retries, payload, etc.) |\n| `batch_size` | int | Batch size for vector generation or retrieval |\n| `top_k` | int | Number of returned candidate passages |\n| `gpu_ids` | str | Specify visible GPU devices, e.g., `\"0,1\"` |\n| `query_instruction` | str | Query prefix (used by instruction-tuning models) |\n| `is_multimodal` | bool | Whether to enable multimodal embedding (e.g., image) |\n| `overwrite` | bool | Whether to overwrite if embedding or index file already exists |\n| `retrieve_thread_num` | int | Concurrent thread number for Web retrieval |\n| `retriever_url` | str | URL of deployed retriever server |\n| `is_demo` | bool | Demo mode s","CLAUDE.md":"# AGENTS.md\n\nThis document is the primary engineering guide for autonomous coding agents working in the `UltraRAG` repository.\n\nUse this file as the source of truth for architecture, conventions, workflows, and safe change patterns.\nIf `CLAUDE.md` exists, it should only point to this file.\n\n---\n\n## 1) Project Identity\n\n`UltraRAG` is a lightweight RAG framework built around the Model Context Protocol (MCP).\nThe key design choice is strict modularization: retrieval, prompting, generation, routing, memory, and evaluation are implemented as independent MCP servers orchestrated by YAML pipelines.\n\nCurrent core metadata:\n\n- Package: `ultrarag`\n- Version: `0.3.0`\n- Python: `>=3.11, <3.13`\n- CLI entrypoint: `ultrarag = ultrarag.client:main`\n- Package manager: `uv` (`[tool.uv] package = true`)\n\n---\n\n## 2) Repository Map (What Matters Most)\n\n```text\nUltraRAG/\n├── src/ultrarag/                    # Installable core package\n│   ├── client.py                    # CLI + pipeline engine + run/build orchestration\n│   ├── server.py                    # UltraRAG_MCP_Server (FastMCP extension)\n│   ├── api.py                       # Python API wrappers (ToolCall, PipelineCall)\n│   ├── cli.py                       # Rich banner and CLI visuals\n│   ├── mcp_logging.py               # Central logging setup\n│   ├── mcp_exceptions.py            # Node.js checks for remote MCP\n│   └── utils.py                     # Subprocess lifecycle helpers\n│\n├── servers/                         # MCP microservices (each server is independent)\n│   ├── retriever/\n│   ├── generation/\n│   ├── prompt/\n│   ├── reranker/\n│   ├── benchmark/\n│   ├── evaluation/\n│   ├── corpus/\n│   ├── memory/\n│   ├── router/\n│   ├── custom/\n│   ├── pageindex/\n│   └── sayhello/\n│\n├── examples/\n│   ├── demos/                       # UI-ready demo pipelines\n│   └── experiments/                 # Experiment/research pipelines\n│\n├── ui/\n│   ├── backend/                     # Flask backend + pipeline manager\n│   └── frontend/                    # Vite + React + TypeScript\n│\n├── docs/                            # Docs and assets\n├── script/                          # Utility scripts (deploy, case study, etc.)\n├── pyproject.toml                   # Dependencies + package metadata\n├── uv.lock                          # Locked dependency graph\n├── Dockerfile*                      # Container variants\n└── .gitignore\n```\n\nImportant generated/derived files:\n\n- `servers/*/server.yaml` (generated by each server `build` tool)\n- `examples/**/parameter/*_parameter.yaml` (pipeline-merged parameters)\n- `examples/**/server/*_server.yaml` (pipeline-merged server config)\n- `output/memory_*.json` (per-run memory snapshots)\n\n---\n\n## 3) Mental Model of the System\n\nThink of UltraRAG as a three-layer system:\n\n1. **Interface layer**: CLI (`ultrarag ...`), UI (`ultrarag show ui`), and Python API (`ToolCall`, `PipelineCall`)\n2. **Orchestration layer**: `src/ultrarag/client.py` (`build`, `load_pipeline_context`, `execute_pipeline`)\n3. **Execution layer**: MCP servers in `servers/*`, each exposing tools/prompts over stdio (or remote MCP proxy)\n\nThe runtime contract is:\n\n- A pipeline YAML declares **which servers** to use and **which steps** to execute.\n- The client resolves I/O dependencies between steps.\n- Each step calls exactly one MCP tool or prompt.\n- Outputs are saved to a shared variable pool and can feed downstream steps.\n\n---\n\n## 4) Two-Phase Execution Lifecycle\n\n### Phase A: Build\n\nCommand:\n\n```bash\nultrarag build <pipeline.yaml>\n```\n\nWhat happens:\n\n- Reads `servers:` from the pipeline YAML.\n- For each referenced server, calls the server's `build` tool.\n- Produces:\n  - `<pipeline_dir>/parameter/<pipeline_name>_parameter.yaml`\n  - `<pipeline_dir>/server/<pipeline_name>_server.yaml`\n\nWhy this matters:\n\n- `build` materializes exact tool/prompt I/O metadata before runtime.\n- UI and runner rely on these generated artifacts for deterministic execution.\n\n### Phase B: Run\n\nCommand:\n\n```bash\nultrarag run <pipeline.yaml> [--param path] [--is_demo]\n```\n\nWhat happens:\n\n- Loads generated server config + parameter config.\n- Creates `fastmcp.Client` transport config for each server.\n- Executes pipeline steps in order, including `loop` and `branch`.\n- Saves intermediate memory snapshots and writes `output/memory_*.json`.\n- Invokes cleanup tools (e.g., tools ending with `vllm_shutdown`) if present.\n\n---\n\n## 5) Pipeline DSL Reference\n\nUltraRAG accepts mixed step forms:\n\n### 5.1 Plain step\n\n```yaml\n- retriever.retriever_search\n```\n\n### 5.2 Step with input/output remapping\n\n```yaml\n- generation.generate:\n    input:\n      prompt_ls: custom_prompt_ls\n    output:\n      ans_ls: final_answer_ls\n```\n\n### 5.3 Loop block\n\n```yaml\n- loop:\n    times: 3\n    steps:\n    - retriever.retriever_search\n    - generation.generate\n```\n\n### 5.4 Branch block\n\n```yaml\n- branch:\n    router:\n    - router.route_query\n    branches:\n      need_retrieval:\n      - retriever.retriever_search\n      direct_answer:\n      - generation.generate\n```\n\n### 5.5 Prompt vs Tool step semantics\n\n- Steps under the `prompt` server call `client.get_prompt(...)`.\n- Non-prompt steps call `client.call_tool(...)`.\n\n---\n\n## 6) Variable Resolution and Data Flow Rules\n\nIn `UltraData`, each tool input value is interpreted by convention:\n\n- `\"$foo\"` -> load from server-local params (`parameter.yaml`)\n- `\"bar\"`  -> read from global variable pool (`global_vars[\"bar\"]`)\n- `\"memory_xxx\"` -> read/write memory lists\n\nOutput handling:\n\n- Tool returns JSON payload -> keys mapped to declared outputs\n- Output remapping (`output:` in pipeline step) is applied at save time\n- Prompt outputs usually produce `prompt_ls`\n\nBranch handling:\n\n- Branch-aware values use wrapped list records with branch-state keys.\n- Internal sentinel (`UNSET`) is used to distinguish \"not filled yet\" from `None`.\n\nMemory handling:\n\n- The engine tracks `memory_*` histories automatically.\n- Final snapshots are serialized to `output/memory_<...>.json`.\n- If a memory server is detected, turn-level memory auto-save can be triggered.\n\n---\n\n## 7) Core Python Modules (Authoritative Guide)\n\n### `src/ultrarag/client.py`\n\nThis is the orchestration heart of the project.\n\nKey responsibilities:\n\n- CLI entrypoint (`main`)\n- UI launch (`launch_ui`) and case-study launch (`launch_case_study`)\n- Config loading (`Configuration`)\n- Pipeline data graph and state (`UltraData`)\n- Build pipeline configs (`build`)\n- Load runtime context (`load_pipeline_context`)\n- Execute step engine (`execute_pipeline`)\n- Run full pipeline (`run`)\n\nImportant runtime behaviors:\n\n- Supports both local python MCP servers (`path.endswith(\".py\")`) and remote MCP endpoints (`http(s)`).\n- For remote MCP, requires Node.js >= 20 and uses `npx -y mcp-remote <url>`.\n- Keeps loop-termination state in `ContextVar` for coroutine safety.\n- Emits structured stream events in demo/UI flows (`step_start`, `step_end`, `token`, `sources`).\n\n### `src/ultrarag/server.py`\n\nDefines `UltraRAG_MCP_Server`, a compatibility wrapper over FastMCP.\n\nKey responsibilities:\n\n- Enhanced `tool()` and `prompt()` registration with `output` metadata support\n- Metadata capture for automatic config generation\n- `build(parameter_file)` to generate per-server `server.yaml`\n- Compatibility filtering for FastMCP signature differences\n\n### `src/ultrarag/api.py`\n\nProvides ergonomic Python-side wrappers:\n\n- `initialize(servers, server_root, log_level)`\n- `ToolCall.server_name.tool_name(...)`\n- `PipelineCall(pipeline_file, parameter_file, log_level)`\n\n### `src/ultrarag/mcp_logging.py`\n\n- Initializes root logger `UltraRAG`\n- Rich console logging + file logging (`logs/<timestamp>.log`)\n- Log level controlled by `log_level` argument and environment\n\n### `src/ultrarag/mcp_exceptions.py`\n\n- Validates local Node.js availability/version\n- Raises `NodeNotInstalledError` / `NodeVersionTooLowError`\n\n### `src/ultrarag/utils.py`\n\n- Subprocess lifecycle helpers\n- POSIX parent-death signal support\n- Windows job object support for child-process cleanup\n\n---\n\n## 8) MCP Server Authoring Contract\n\nEach server follows this shape:\n\n```text\nservers/<name>/\n├── parameter.yaml\n├── server.yaml            # generated\n└── src/<name>.py\n```\n\n### 8.1 Registration styles\n\nUse either:\n\n1. Decorator style\n2. Class-bound method registration style\n\nBoth are valid in this codebase.\n\n### 8.2 `output=` grammar\n\nCanonical form:\n\n```text\ninput1,input2,$param_a -> output1,output2\n```\n\nRules:\n\n- Left side maps function args to pipeline inputs.\n- Right side defines expected output keys from returned dict/JSON.\n- `-> None` means no output variables.\n- `$param` means value comes from server parameter file.\n\n### 8.3 Return payload expectations\n\n- For tools: return JSON-serializable dict payloads matching declared output keys.\n- For prompts: return prompt messages (typically list-like prompt payloads consumed by `get_prompt`).\n\n### 8.4 Entrypoint requirement\n\nServer modules should end with:\n\n```python\nif __name__ == \"__main__\":\n    app.run(transport=\"stdio\")\n```\n\n---\n\n## 9) Retriever/Generation/Prompt Specific Notes\n\n### Retriever (`servers/retriever`)\n\n- Supports multiple retrieval modes:\n  - Dense retrieval\n  - BM25\n  - Web search\n  - Project-memory retrieval\n- Index backends are pluggable via factory:\n  - `faiss`\n  - `milvus`\n  - `qdrant`\n- Web search backends are pluggable via factory:\n  - `exa`\n  - `tavily`\n  - `zhipuai`\n  - `serply`\n\n### Generation (`servers/generation`)\n\n- Supports generation backends including `openai`, `vllm`, and hf workflows.\n- Provides explicit cleanup tool `vllm_shutdown`.\n- Demo mode can use local streaming generation service.\n\n### Prompt (`servers/prompt`)\n\n- Uses `SandboxedEnvironment` from Jinja2 for safer rendering.\n- Validates template paths to reduce traversal risk.\n- Escapes string inputs before template rendering.\n\n---\n\n## 10) UI Backend Architecture (`ui/backend`)\n\n### `app.py`\n\n- Flask app factory (`create_app`)\n- Serves frontend static assets\n- Exposes chat/pipeline/KB/auth endpoints\n- Reads optional frontend override via `ULTRARAG_FRONTEND_DIR`\n\n### `pipeline_manager.py`\n\nThis module is large and central to UI behavior.\n\nMajor responsibilities:\n\n- Session lifecycle and streaming chat management\n- Background chat task management\n- Pipeline CRUD (list/load/save/rename/delete)\n- Parameter load/save/build wrappers\n- Knowledge base file ingest and pipeline triggering\n- Memory synchronization to per-user KB collections\n- Optional server introspection via AST stub generation if `server.yaml` is missing\n\nNotable behavior:\n\n- Applies defensive patches to suppress noisy closed-event-loop teardown logs.\n- Uses a queue bridge for async-to-sync SSE event streaming.\n- Supports automatic memory-to-KB sync for pipelines that include memory components.\n\n---\n\n## 11) Storage Model and Paths\n\nDefault UI storage root:\n\n- `ui/storage`\n\nCan be overridden by:\n\n- `ULTRARAG_UI_STORAGE_ROOT`\n\nKey subpaths:\n\n- `db/users.sqlite3`\n- `chat_sessions/`\n- `knowledge_base/raw|corpus|chunks|index`\n- `memory/`\n- `knowledge_base/_memory_sync`\n\nRuntime outputs:\n\n- `output/memory_*.json`\n- `logs/*.log`\n\n---\n\n## 12) Environment Variables You Should Know\n\n- `ULTRARAG_UI_STORAGE_ROOT`: override UI storage root\n- `ULTRARAG_FRONTEND_DIR`: override frontend static directory\n- `ULTRARAG_SESSION_TIMEOUT`: foreground chat session timeout\n- `ULTRARAG_BG_SESSION_TIMEOUT`: background session timeout\n- `ULTRARAG_LOG_TS`: custom timestamp seed for log file naming\n- `log_level`: consumed by core logger initialization\n\n---\n\n## 13) Dependency Model\n\nInstall tiers from `pyproject.toml`:\n\n- Core install: no extras\n- `retriever` extra\n- `generation` extra\n- `evaluation` extra\n- `corpus` extra\n- `all` extra (union)\n\nTypical commands:\n\n```bash\nuv sync\nuv sync --extra retriever\nuv sync --extra generation\nuv sync --all-extras\n```\n\nDevelopment dependencies include:\n\n- `ruff`\n- `ipython`\n- `jupyter`\n- `pytest`\n\n---\n\n## 14) CLI Commands (Canonical)\n\n```bash\nultrarag build <pipeline.yaml>\nultrarag run <pipeline.yaml> [--param <parameter.yaml>] [--log_level info|debug|warn|error] [--is_demo]\nultrarag show ui [--host 127.0.0.1] [--port 5050]\nultrarag show case [--config_path <memory.json>] [--host 127.0.0.1] [--port 8080]\n```\n\nMinimal smoke check:\n\n```bash\nultrarag run examples/experiments/sayhello.yaml\n```\n\n---\n\n## 15) Docker Variants\n\n- `Dockerfile`: full image (builds frontend, installs all extras)\n- `Dockerfile.base-cpu`: CPU base image\n- `Dockerfile.base-gpu`: GPU base image\n\nAll variants start UI with:\n\n```bash\nultrarag show ui --port 5050 --host 0.0.0.0\n```\n\n---\n\n## 16) Development Playbooks\n\n### 16.1 Add a new MCP server\n\n1. Create `servers/<name>/parameter.yaml`\n2. Implement `servers/<name>/src/<name>.py`\n3. Register tools/prompts via `app.tool` / `app.prompt` (or class-bound registration)\n4. Ensure `app.run(transport=\"stdio\")` exists\n5. Add server to `servers:` in a pipeline YAML\n6. Run `ultrarag build <pipeline.yaml>`\n\n### 16.2 Add a new tool to an existing server\n\n1. Implement function/method\n2. Register with explicit `output=...` contract\n3. Ensure return payload keys match outputs\n4. Update relevant parameter keys in `parameter.yaml` if using `$...`\n5. Add the step in pipeline YAML and rebuild\n\n### 16.3 Add a retriever index backend\n\n1. Implement backend in `servers/retriever/src/index_backends/`\n2. Follow `BaseIndexBackend` contract\n3. Register in `_INDEX_BACKENDS` map in `index_backends/__init__.py`\n\n### 16.4 Add a web-search backend\n\n1. Implement backend in `servers/retriever/src/websearch_backends/`\n2. Follow `BaseWebSearchBackend` contract\n3. Register in `_WEBSEARCH_BACKENDS` map\n\n### 16.5 Modify UI pipeline behavior\n\nPrimary files:\n\n- `ui/backend/app.py`\n- `ui/backend/pipeline_manager.py`\n\nIf touching build/runtime semantics, cross-check against:\n\n- `src/ultrarag/client.py`\n\n---\n\n## 17) Coding Standards (Repository-Conformant)\n\n- Use type hints for function signatures.\n- Prefer `pathlib.Path` for filesystem paths.\n- Use `yaml.safe_load` / `yaml.safe_dump`.\n- Use project logger (`get_logger` or `app.logger`) instead of `print`.\n- Keep tool outputs deterministic and JSON-serializable.\n- Keep imports grouped: stdlib -> third-party -> local.\n- Keep async boundaries explicit (`async`/`await`).\n\n---\n\n## 18) What Not To Edit Blindly\n\nTreat these as generated or runtime artifacts unless intentionally regenerating:\n\n- `servers/*/server.yaml`\n- `examples/**/parameter/*_parameter.yaml`\n- `examples/**/server/*_server.yaml`\n- `output/*`\n- `logs/*`\n- `ui/storage/*` runtime data\n\nAlso avoid committing secrets:\n\n- `.env`\n- any credential-bearing local config\n\n---\n\n## 19) Common Failure Modes and Fixes\n\n### Error: server file not found\n\n- Check `servers.<name>` path in pipeline YAML.\n- Ensure server entry script exists at `servers/<name>/src/<name>.py` (or update path in config).\n\n### Error: missing variable in pipeline execution\n\n- Verify output key names from upstream tool match downstream input names.\n- Verify remapping under step-level `output:` is correct.\n- Verify `$param` keys exist in that server's parameter config.\n\n### Remote MCP server fails to start\n\n- Ensure Node.js >= 20.\n- Confirm `npx` is available.\n- Confirm remote URL in server path is reachable.\n\n### Build succeeds but UI cannot list tools\n\n- Ensure `server.yaml` exists or can be inferred by AST parsing.\n- Check for unusual dynamic registration patterns that static analysis cannot infer.\n\n### No final answer in chat\n\n- Inspect `output/memory_*.json`.\n- Check whether generation step ran and produced `ans_ls`.\n- Check stream events in UI path (`step_start`, `step_end`, `sources`, `token`, `final`).\n\n---\n\n## 20) Validation Checklist for Agents\n\nBefore finalizing any non-trivial change:\n\n1. Build the affected pipeline:\n   - `ultrarag build <pipeline.yaml>`\n2. Run a relevant smoke case:\n   - `ultrarag run <pipeline.yaml>`\n3. If UI behavior changed:\n   - run `ultrarag show ui` and verify route behavior\n4. If retriever/generation backends changed:\n   - validate parameter schema keys and output names\n5. Keep generated artifacts intentional:\n   - do not accidentally commit transient runtime outputs\n\n---\n\n## 21) Minimal Quickstart for New Agents\n\n```bash\n# 1) Install dependencies\nuv sync --all-extras\n\n# 2) Smoke test\nultrarag run examples/experiments/sayhello.yaml\n\n# 3) Build and run a demo pipeline\nultrarag build examples/demos/LLM.yaml\nultrarag run examples/demos/LLM.yaml\n\n# 4) Launch UI\nultrarag show ui --host 127.0.0.1 --port 5050\n```\n\n---\n\n## 22) Final Notes\n\n- This repository is orchestration-first: correctness depends heavily on I/O naming consistency across tools and pipeline steps.\n- Most regressions come from mismatched variable names, stale generated configs, or incomplete parameter updates.\n- When in doubt, inspect `src/ultrarag/client.py` first: it is the execution truth.\n"},"files":{"AGENTS.md":"# AGENTS.md\n\nThis document is the primary engineering guide for autonomous coding agents working in the `UltraRAG` repository.\n\nUse this file as the source of truth for architecture, conventions, workflows, and safe change patterns.\nIf `CLAUDE.md` exists, it should only point to this file.\n\n---\n\n## 1) Project Identity\n\n`UltraRAG` is a lightweight RAG framework built around the Model Context Protocol (MCP).\nThe key design choice is strict modularization: retrieval, prompting, generation, routing, memory, and evaluation are implemented as independent MCP servers orchestrated by YAML pipelines.\n\nCurrent core metadata:\n\n- Package: `ultrarag`\n- Version: `0.3.0`\n- Python: `>=3.11, <3.13`\n- CLI entrypoint: `ultrarag = ultrarag.client:main`\n- Package manager: `uv` (`[tool.uv] package = true`)\n\n---\n\n## 2) Repository Map (What Matters Most)\n\n```text\nUltraRAG/\n├── src/ultrarag/                    # Installable core package\n│   ├── client.py                    # CLI + pipeline engine + run/build orchestration\n│   ├── server.py                    # UltraRAG_MCP_Server (FastMCP extension)\n│   ├── api.py                       # Python API wrappers (ToolCall, PipelineCall)\n│   ├── cli.py                       # Rich banner and CLI visuals\n│   ├── mcp_logging.py               # Central logging setup\n│   ├── mcp_exceptions.py            # Node.js checks for remote MCP\n│   └── utils.py                     # Subprocess lifecycle helpers\n│\n├── servers/                         # MCP microservices (each server is independent)\n│   ├── retriever/\n│   ├── generation/\n│   ├── prompt/\n│   ├── reranker/\n│   ├── benchmark/\n│   ├── evaluation/\n│   ├── corpus/\n│   ├── memory/\n│   ├── router/\n│   ├── custom/\n│   ├── pageindex/\n│   └── sayhello/\n│\n├── examples/\n│   ├── demos/                       # UI-ready demo pipelines\n│   └── experiments/                 # Experiment/research pipelines\n│\n├── ui/\n│   ├── backend/                     # Flask backend + pipeline manager\n│   └── frontend/                    # Vite + React + TypeScript\n│\n├── docs/                            # Docs and assets\n├── script/                          # Utility scripts (deploy, case study, etc.)\n├── pyproject.toml                   # Dependencies + package metadata\n├── uv.lock                          # Locked dependency graph\n├── Dockerfile*                      # Container variants\n└── .gitignore\n```\n\nImportant generated/derived files:\n\n- `servers/*/server.yaml` (generated by each server `build` tool)\n- `examples/**/parameter/*_parameter.yaml` (pipeline-merged parameters)\n- `examples/**/server/*_server.yaml` (pipeline-merged server config)\n- `output/memory_*.json` (per-run memory snapshots)\n\n---\n\n## 3) Mental Model of the System\n\nThink of UltraRAG as a three-layer system:\n\n1. **Interface layer**: CLI (`ultrarag ...`), UI (`ultrarag show ui`), and Python API (`ToolCall`, `PipelineCall`)\n2. **Orchestration layer**: `src/ultrarag/client.py` (`build`, `load_pipeline_context`, `execute_pipeline`)\n3. **Execution layer**: MCP servers in `servers/*`, each exposing tools/prompts over stdio (or remote MCP proxy)\n\nThe runtime contract is:\n\n- A pipeline YAML declares **which servers** to use and **which steps** to execute.\n- The client resolves I/O dependencies between steps.\n- Each step calls exactly one MCP tool or prompt.\n- Outputs are saved to a shared variable pool and can feed downstream steps.\n\n---\n\n## 4) Two-Phase Execution Lifecycle\n\n### Phase A: Build\n\nCommand:\n\n```bash\nultrarag build <pipeline.yaml>\n```\n\nWhat happens:\n\n- Reads `servers:` from the pipeline YAML.\n- For each referenced server, calls the server's `build` tool.\n- Produces:\n  - `<pipeline_dir>/parameter/<pipeline_name>_parameter.yaml`\n  - `<pipeline_dir>/server/<pipeline_name>_server.yaml`\n\nWhy this matters:\n\n- `build` materializes exact tool/prompt I/O metadata before runtime.\n- UI and runner rely on these generated artifacts for deterministic execution.\n\n### Phase B: Run\n\nCommand:\n\n```bash\nultrarag run <pipeline.yaml> [--param path] [--is_demo]\n```\n\nWhat happens:\n\n- Loads generated server config + parameter config.\n- Creates `fastmcp.Client` transport config for each server.\n- Executes pipeline steps in order, including `loop` and `branch`.\n- Saves intermediate memory snapshots and writes `output/memory_*.json`.\n- Invokes cleanup tools (e.g., tools ending with `vllm_shutdown`) if present.\n\n---\n\n## 5) Pipeline DSL Reference\n\nUltraRAG accepts mixed step forms:\n\n### 5.1 Plain step\n\n```yaml\n- retriever.retriever_search\n```\n\n### 5.2 Step with input/output remapping\n\n```yaml\n- generation.generate:\n    input:\n      prompt_ls: custom_prompt_ls\n    output:\n      ans_ls: final_answer_ls\n```\n\n### 5.3 Loop block\n\n```yaml\n- loop:\n    times: 3\n    steps:\n    - retriever.retriever_search\n    - generation.generate\n```\n\n### 5.4 Branch block\n\n```yaml\n- branch:\n    router:\n    - router.route_query\n    branches:\n      need_retrieval:\n      - retriever.retriever_search\n      direct_answer:\n      - generation.generate\n```\n\n### 5.5 Prompt vs Tool step semantics\n\n- Steps under the `prompt` server call `client.get_prompt(...)`.\n- Non-prompt steps call `client.call_tool(...)`.\n\n---\n\n## 6) Variable Resolution and Data Flow Rules\n\nIn `UltraData`, each tool input value is interpreted by convention:\n\n- `\"$foo\"` -> load from server-local params (`parameter.yaml`)\n- `\"bar\"`  -> read from global variable pool (`global_vars[\"bar\"]`)\n- `\"memory_xxx\"` -> read/write memory lists\n\nOutput handling:\n\n- Tool returns JSON payload -> keys mapped to declared outputs\n- Output remapping (`output:` in pipeline step) is applied at save time\n- Prompt outputs usually produce `prompt_ls`\n\nBranch handling:\n\n- Branch-aware values use wrapped list records with branch-state keys.\n- Internal sentinel (`UNSET`) is used to distinguish \"not filled yet\" from `None`.\n\nMemory handling:\n\n- The engine tracks `memory_*` histories automatically.\n- Final snapshots are serialized to `output/memory_<...>.json`.\n- If a memory server is detected, turn-level memory auto-save can be triggered.\n\n---\n\n## 7) Core Python Modules (Authoritative Guide)\n\n### `src/ultrarag/client.py`\n\nThis is the orchestration heart of the project.\n\nKey responsibilities:\n\n- CLI entrypoint (`main`)\n- UI launch (`launch_ui`) and case-study launch (`launch_case_study`)\n- Config loading (`Configuration`)\n- Pipeline data graph and state (`UltraData`)\n- Build pipeline configs (`build`)\n- Load runtime context (`load_pipeline_context`)\n- Execute step engine (`execute_pipeline`)\n- Run full pipeline (`run`)\n\nImportant runtime behaviors:\n\n- Supports both local python MCP servers (`path.endswith(\".py\")`) and remote MCP endpoints (`http(s)`).\n- For remote MCP, requires Node.js >= 20 and uses `npx -y mcp-remote <url>`.\n- Keeps loop-termination state in `ContextVar` for coroutine safety.\n- Emits structured stream events in demo/UI flows (`step_start`, `step_end`, `token`, `sources`).\n\n### `src/ultrarag/server.py`\n\nDefines `UltraRAG_MCP_Server`, a compatibility wrapper over FastMCP.\n\nKey responsibilities:\n\n- Enhanced `tool()` and `prompt()` registration with `output` metadata support\n- Metadata capture for automatic config generation\n- `build(parameter_file)` to generate per-server `server.yaml`\n- Compatibility filtering for FastMCP signature differences\n\n### `src/ultrarag/api.py`\n\nProvides ergonomic Python-side wrappers:\n\n- `initialize(servers, server_root, log_level)`\n- `ToolCall.server_name.tool_name(...)`\n- `PipelineCall(pipeline_file, parameter_file, log_level)`\n\n### `src/ultrarag/mcp_logging.py`\n\n- Initializes root logger `UltraRAG`\n- Rich console logging + file logging (`logs/<timestamp>.log`)\n- Log level controlled by `log_level` argument and environment\n\n### `src/ultrarag/mcp_exceptions.py`\n\n- Validates local Node.js availability/version\n- Raises `NodeNotInstalledError` / `NodeVersionTooLowError`\n\n### `src/ultrarag/utils.py`\n\n- Subprocess lifecycle helpers\n- POSIX parent-death signal support\n- Windows job object support for child-process cleanup\n\n---\n\n## 8) MCP Server Authoring Contract\n\nEach server follows this shape:\n\n```text\nservers/<name>/\n├── parameter.yaml\n├── server.yaml            # generated\n└── src/<name>.py\n```\n\n### 8.1 Registration styles\n\nUse either:\n\n1. Decorator style\n2. Class-bound method registration style\n\nBoth are valid in this codebase.\n\n### 8.2 `output=` grammar\n\nCanonical form:\n\n```text\ninput1,input2,$param_a -> output1,output2\n```\n\nRules:\n\n- Left side maps function args to pipeline inputs.\n- Right side defines expected output keys from returned dict/JSON.\n- `-> None` means no output variables.\n- `$param` means value comes from server parameter file.\n\n### 8.3 Return payload expectations\n\n- For tools: return JSON-serializable dict payloads matching declared output keys.\n- For prompts: return prompt messages (typically list-like prompt payloads consumed by `get_prompt`).\n\n### 8.4 Entrypoint requirement\n\nServer modules should end with:\n\n```python\nif __name__ == \"__main__\":\n    app.run(transport=\"stdio\")\n```\n\n---\n\n## 9) Retriever/Generation/Prompt Specific Notes\n\n### Retriever (`servers/retriever`)\n\n- Supports multiple retrieval modes:\n  - Dense retrieval\n  - BM25\n  - Web search\n  - Project-memory retrieval\n- Index backends are pluggable via factory:\n  - `faiss`\n  - `milvus`\n  - `qdrant`\n- Web search backends are pluggable via factory:\n  - `exa`\n  - `tavily`\n  - `zhipuai`\n  - `serply`\n\n### Generation (`servers/generation`)\n\n- Supports generation backends including `openai`, `vllm`, and hf workflows.\n- Provides explicit cleanup tool `vllm_shutdown`.\n- Demo mode can use local streaming generation service.\n\n### Prompt (`servers/prompt`)\n\n- Uses `SandboxedEnvironment` from Jinja2 for safer rendering.\n- Validates template paths to reduce traversal risk.\n- Escapes string inputs before template rendering.\n\n---\n\n## 10) UI Backend Architecture (`ui/backend`)\n\n### `app.py`\n\n- Flask app factory (`create_app`)\n- Serves frontend static assets\n- Exposes chat/pipeline/KB/auth endpoints\n- Reads optional frontend override via `ULTRARAG_FRONTEND_DIR`\n\n### `pipeline_manager.py`\n\nThis module is large and central to UI behavior.\n\nMajor responsibilities:\n\n- Session lifecycle and streaming chat management\n- Background chat task management\n- Pipeline CRUD (list/load/save/rename/delete)\n- Parameter load/save/build wrappers\n- Knowledge base file ingest and pipeline triggering\n- Memory synchronization to per-user KB collections\n- Optional server introspection via AST stub generation if `server.yaml` is missing\n\nNotable behavior:\n\n- Applies defensive patches to suppress noisy closed-event-loop teardown logs.\n- Uses a queue bridge for async-to-sync SSE event streaming.\n- Supports automatic memory-to-KB sync for pipelines that include memory components.\n\n---\n\n## 11) Storage Model and Paths\n\nDefault UI storage root:\n\n- `ui/storage`\n\nCan be overridden by:\n\n- `ULTRARAG_UI_STORAGE_ROOT`\n\nKey subpaths:\n\n- `db/users.sqlite3`\n- `chat_sessions/`\n- `knowledge_base/raw|corpus|chunks|index`\n- `memory/`\n- `knowledge_base/_memory_sync`\n\nRuntime outputs:\n\n- `output/memory_*.json`\n- `logs/*.log`\n\n---\n\n## 12) Environment Variables You Should Know\n\n- `ULTRARAG_UI_STORAGE_ROOT`: override UI storage root\n- `ULTRARAG_FRONTEND_DIR`: override frontend static directory\n- `ULTRARAG_SESSION_TIMEOUT`: foreground chat session timeout\n- `ULTRARAG_BG_SESSION_TIMEOUT`: background session timeout\n- `ULTRARAG_LOG_TS`: custom timestamp seed for log file naming\n- `log_level`: consumed by core logger initialization\n\n---\n\n## 13) Dependency Model\n\nInstall tiers from `pyproject.toml`:\n\n- Core install: no extras\n- `retriever` extra\n- `generation` extra\n- `evaluation` extra\n- `corpus` extra\n- `all` extra (union)\n\nTypical commands:\n\n```bash\nuv sync\nuv sync --extra retriever\nuv sync --extra generation\nuv sync --all-extras\n```\n\nDevelopment dependencies include:\n\n- `ruff`\n- `ipython`\n- `jupyter`\n- `pytest`\n\n---\n\n## 14) CLI Commands (Canonical)\n\n```bash\nultrarag build <pipeline.yaml>\nultrarag run <pipeline.yaml> [--param <parameter.yaml>] [--log_level info|debug|warn|error] [--is_demo]\nultrarag show ui [--host 127.0.0.1] [--port 5050]\nultrarag show case [--config_path <memory.json>] [--host 127.0.0.1] [--port 8080]\n```\n\nMinimal smoke check:\n\n```bash\nultrarag run examples/experiments/sayhello.yaml\n```\n\n---\n\n## 15) Docker Variants\n\n- `Dockerfile`: full image (builds frontend, installs all extras)\n- `Dockerfile.base-cpu`: CPU base image\n- `Dockerfile.base-gpu`: GPU base image\n\nAll variants start UI with:\n\n```bash\nultrarag show ui --port 5050 --host 0.0.0.0\n```\n\n---\n\n## 16) Development Playbooks\n\n### 16.1 Add a new MCP server\n\n1. Create `servers/<name>/parameter.yaml`\n2. Implement `servers/<name>/src/<name>.py`\n3. Register tools/prompts via `app.tool` / `app.prompt` (or class-bound registration)\n4. Ensure `app.run(transport=\"stdio\")` exists\n5. Add server to `servers:` in a pipeline YAML\n6. Run `ultrarag build <pipeline.yaml>`\n\n### 16.2 Add a new tool to an existing server\n\n1. Implement function/method\n2. Register with explicit `output=...` contract\n3. Ensure return payload keys match outputs\n4. Update relevant parameter keys in `parameter.yaml` if using `$...`\n5. Add the step in pipeline YAML and rebuild\n\n### 16.3 Add a retriever index backend\n\n1. Implement backend in `servers/retriever/src/index_backends/`\n2. Follow `BaseIndexBackend` contract\n3. Register in `_INDEX_BACKENDS` map in `index_backends/__init__.py`\n\n### 16.4 Add a web-search backend\n\n1. Implement backend in `servers/retriever/src/websearch_backends/`\n2. Follow `BaseWebSearchBackend` contract\n3. Register in `_WEBSEARCH_BACKENDS` map\n\n### 16.5 Modify UI pipeline behavior\n\nPrimary files:\n\n- `ui/backend/app.py`\n- `ui/backend/pipeline_manager.py`\n\nIf touching build/runtime semantics, cross-check against:\n\n- `src/ultrarag/client.py`\n\n---\n\n## 17) Coding Standards (Repository-Conformant)\n\n- Use type hints for function signatures.\n- Prefer `pathlib.Path` for filesystem paths.\n- Use `yaml.safe_load` / `yaml.safe_dump`.\n- Use project logger (`get_logger` or `app.logger`) instead of `print`.\n- Keep tool outputs deterministic and JSON-serializable.\n- Keep imports grouped: stdlib -> third-party -> local.\n- Keep async boundaries explicit (`async`/`await`).\n\n---\n\n## 18) What Not To Edit Blindly\n\nTreat these as generated or runtime artifacts unless intentionally regenerating:\n\n- `servers/*/server.yaml`\n- `examples/**/parameter/*_parameter.yaml`\n- `examples/**/server/*_server.yaml`\n- `output/*`\n- `logs/*`\n- `ui/storage/*` runtime data\n\nAlso avoid committing secrets:\n\n- `.env`\n- any credential-bearing local config\n\n---\n\n## 19) Common Failure Modes and Fixes\n\n### Error: server file not found\n\n- Check `servers.<name>` path in pipeline YAML.\n- Ensure server entry script exists at `servers/<name>/src/<name>.py` (or update path in config).\n\n### Error: missing variable in pipeline execution\n\n- Verify output key names from upstream tool match downstream input names.\n- Verify remapping under step-level `output:` is correct.\n- Verify `$param` keys exist in that server's parameter config.\n\n### Remote MCP server fails to start\n\n- Ensure Node.js >= 20.\n- Confirm `npx` is available.\n- Confirm remote URL in server path is reachable.\n\n### Build succeeds but UI cannot list tools\n\n- Ensure `server.yaml` exists or can be inferred by AST parsing.\n- Check for unusual dynamic registration patterns that static analysis cannot infer.\n\n### No final answer in chat\n\n- Inspect `output/memory_*.json`.\n- Check whether generation step ran and produced `ans_ls`.\n- Check stream events in UI path (`step_start`, `step_end`, `sources`, `token`, `final`).\n\n---\n\n## 20) Validation Checklist for Agents\n\nBefore finalizing any non-trivial change:\n\n1. Build the affected pipeline:\n   - `ultrarag build <pipeline.yaml>`\n2. Run a relevant smoke case:\n   - `ultrarag run <pipeline.yaml>`\n3. If UI behavior changed:\n   - run `ultrarag show ui` and verify route behavior\n4. If retriever/generation backends changed:\n   - validate parameter schema keys and output names\n5. Keep generated artifacts intentional:\n   - do not accidentally commit transient runtime outputs\n\n---\n\n## 21) Minimal Quickstart for New Agents\n\n```bash\n# 1) Install dependencies\nuv sync --all-extras\n\n# 2) Smoke test\nultrarag run examples/experiments/sayhello.yaml\n\n# 3) Build and run a demo pipeline\nultrarag build examples/demos/LLM.yaml\nultrarag run examples/demos/LLM.yaml\n\n# 4) Launch UI\nultrarag show ui --host 127.0.0.1 --port 5050\n```\n\n---\n\n## 22) Final Notes\n\n- This repository is orchestration-first: correctness depends heavily on I/O naming consistency across tools and pipeline steps.\n- Most regressions come from mismatched variable names, stale generated configs, or incomplete parameter updates.\n- When in doubt, inspect `src/ultrarag/client.py` first: it is the execution truth.\n","docs/llms.txt":"This file is a merged representation of the entire codebase, combining all repository files into a single document.\nGenerated by Repopack on: 2026-01-18T09:46:51.430Z\n\n================================================================\nFile Summary\n================================================================\n\nPurpose:\n--------\nThis file contains a packed representation of the entire repository's contents.\nIt is designed to be easily consumable by AI systems for analysis, code review,\nor other automated processes.\n\nFile Format:\n------------\nThe content is organized as follows:\n1. This summary section\n2. Repository information\n3. Repository structure\n4. Multiple file entries, each consisting of:\n  a. A separator line (================)\n  b. The file path (File: path/to/file)\n  c. Another separator line\n  d. The full contents of the file\n  e. A blank line\n\nUsage Guidelines:\n-----------------\n- This file should be treated as read-only. Any changes should be made to the\n  original repository files, not this packed version.\n- When processing this file, use the file path to distinguish\n  between different files in the repository.\n- Be aware that this file may contain sensitive information. Handle it with\n  the same level of security as you would the original repository.\n\nNotes:\n------\n- Some files may have been excluded based on .gitignore rules and Repopack's\n  configuration.\n- Binary files are not included in this packed representation. Please refer to\n  the Repository Structure section for a complete list of file paths, including\n  binary files.\n\nAdditional Info:\n----------------\n\nFor more information about Repopack, visit: https://github.com/yamadashy/repopack\n\n================================================================\nRepository Structure\n================================================================\npages/\n  en/\n    api/\n      benchmark.mdx\n      cli.mdx\n      corpus.mdx\n      custom.mdx\n      evaluation.mdx\n      generation.mdx\n      prompt.mdx\n      reranker.mdx\n      retriever.mdx\n      router.mdx\n    demo/\n      deepresearch.mdx\n      lightresearch.mdx\n      llm.mdx\n      rag.mdx\n    develop_guide/\n      case_study.mdx\n      code_integration.mdx\n      dataset.mdx\n      debug.mdx\n      parallel.mdx\n    getting_started/\n      installation.mdx\n      introduction.mdx\n      quick_start.mdx\n      update.mdx\n    pipeline/\n      light_deepresearch.mdx\n      rag.mdx\n      search_o1.mdx\n      visrag.mdx\n    rag_client/\n      branch.mdx\n      data_and_params.mdx\n      loop.mdx\n      multi_agents.mdx\n      pipeline.mdx\n      serial.mdx\n    rag_servers/\n      benchmark.mdx\n      corpus.mdx\n      custom.mdx\n      evaluation.mdx\n      generation.mdx\n      overview.mdx\n      prompt.mdx\n      reranker.mdx\n      retriever.mdx\n      router.mdx\n    ui/\n      prepare.mdx\n      start.mdx\n\n================================================================\nRepository Files\n================================================================\n\n================\nFile: pages/en/api/benchmark.mdx\n================\n---\ntitle: \"Benchmark\"\nicon: \"chart-line\"\n---\n\n## `get_data`\n\n**Signature**\n```python\n@app.tool(output=\"benchmark->q_ls,gt_ls\")\ndef get_data(benchmark: Dict[str, Any]) -> Dict[str, List[Any]]\n```\n\n**Function**\n- Multi-format Loading: Supports loading evaluation datasets in `.jsonl`, `.json`, or `.parquet` formats from local storage.\n- Dynamic Field Mapping: Uses `key_map` to map different column names in raw data (such as `question`, `answer`) to standardized output keys (usually `q_ls` and `gt_ls`).\n- Data Preprocessing: Built-in support for random shuffling (`shuffle`) and quantity truncation (`limit`).\n- Used in Demos to receive user input, treating it as a piece of data (`q_ls`).\n\n**Output Format (JSON)**\n```json\n{\n  \"q_ls\": [\"Question 1\", \"Question 2\"],\n  \"gt_ls\": [[\"Answer A1\", \"Answer A2\"], [\"Answer B\"]]\n}\n```\n\n---\n\n## Configuration\n\n```yaml servers/benchmark/parameter.yaml icon=\"/images/yaml.svg\"\nbenchmark:\n  name: nq\n  path: data/sample_nq_10.jsonl\n  key_map:\n    q_ls: question\n    gt_ls: golden_answers\n  shuffle: false\n  seed: 42\n  limit: -1\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `name` | str | Evaluation set name, used only for logging and identification (Example: `nq`) |\n| `path` | str | Data file path, supports `.jsonl`, `.json`, `.parquet` |\n| `key_map` | dict | Field mapping table, mapping raw fields to tool output keys |\n|  | `q_ls` | str| Raw field name mapped to Question List (e.g., question column in file) |\n|  | `gt_ls` | str| Raw field name mapped to Golden Answer List (e.g., golden_answers column in file) |\n| `shuffle` | bool | Whether to shuffle sample order (default `false`) |\n| `seed` | int | Random seed (effective when `shuffle=true`) |\n| `limit` | int | Upper limit of sampled data items. Default is -1 (load all), positive integer means truncation of first N items |\n\n================\nFile: pages/en/api/cli.mdx\n================\n---\ntitle: \"CLI\"\nicon: \"command\"\n---\n\n## help — Command Overview\n\n```bash\nultrarag --help\n```\n\n```text\nusage: ultrarag [-h] {build,run,show} ...\n\nUltraRAG CLI\n\npositional arguments:\n  {build,run,show}\n    build           Build the configuration\n    run             Run the pipeline with the given configuration\n    show            Show ui interfaces\n\noptions:\n  -h, --help        show this help message and exit\n```\n\n---\n\n## build — Build Configuration\n\n**Purpose**: Automatically generate a unified parameter configuration file based on `pipeline.yaml`.\n\n```bash\nultrarag build <CONFIG> [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `<CONFIG>` | Pipeline configuration file path, e.g., `examples/pipeline.yaml` |\n| `--log_level` | Log level, default `INFO` |\n\n---\n\n## run — Execute Pipeline\n\n**Purpose**: Start Pipeline execution.\n\n```bash\nultrarag run <CONFIG> [--param <PARAM_FILE>] [--is_demo] [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `<CONFIG>` | Path to `pipeline.yaml` to be executed |\n| `--param <PARAM_FILE>` | Custom parameter file path, overrides default files under `parameter/` |\n| `--is_demo` | Enable demo mode |\n| `--log_level` | Log level, default `INFO` |\n\n---\n\n## show ui — Start Web Control Interface\n\n**Purpose**: Start UI interface.\n\n```bash\nultrarag show ui [--host 127.0.0.1] [--port 5050] [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `--host` | Web service binding IP (default `127.0.0.1`) |\n| `--port` | Web service port (default `5050`) |\n| `--log_level` | Log level, default `INFO` |\n\nAccess `http://<host>:<port>` after startup to use the UI.\n\n================\nFile: pages/en/api/corpus.mdx\n================\n---\ntitle: \"Corpus\"\nicon: \"file\"\n---\n\n\n## `build_text_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,text_corpus_save_path->None\")\nasync def build_text_corpus(parse_file_path: str, text_corpus_save_path: str) -> None\n```\n\n**Function**\n- Supports .txt / .md;\n- Supports .docx (reads paragraphs and tables);\n- Supports .pdf / .xps / .oxps / .epub / .mobi / .fb2 (pure text extraction via pymupdf).\n- Recursively processes in directory mode.\n\n**Output Format (JSONL)**\n```json\n{\"id\": \"<stem>\", \"title\": \"<stem>\", \"contents\": \"<full text>\"}\n```\n---\n\n## `build_image_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,image_corpus_save_path->None\")\nasync def build_image_corpus(parse_file_path: str, image_corpus_save_path: str) -> None\n```\n\n**Function**\n- **Only supports PDF**: Renders each page as JPG (RGB) at 144DPI and validates file validity.\n- Recursively processes in directory mode.\n\n**Output Index (JSONL)**\n```json\n{\"id\": 0, \"image_id\": \"paper/page_0.jpg\", \"image_path\": \"image/paper/page_0.jpg\"}\n```\n---\n\n## `mineru_parse`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,mineru_dir,mineru_extra_params->None\")\nasync def mineru_parse(\n    parse_file_path: str, \n    mineru_dir: str, \n    mineru_extra_params: Optional[Dict[str, Any]] = None\n) -> None\n```\n\n**Function**\n- Calls CLI `mineru` to structurally parse PDF/directory and outputs to `mineru_dir`.\n\n---\n\n## `build_mineru_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"mineru_dir,parse_file_path,text_corpus_save_path,image_corpus_save_path->None\")\nasync def build_mineru_corpus(\n    mineru_dir: str, \n    parse_file_path: str, \n    text_corpus_save_path: str, \n    image_corpus_save_path: str\n) -> None\n```\n\n**Function**\n- Aggregates MinerU parsing artifacts into **Text Corpus JSONL** and **Image Index JSONL**.\n\n\n**Output Format (JSONL)**\n- Text:\n```json\n{\"id\": \"<stem>\", \"title\": \"<stem>\", \"contents\": \"<markdown full text>\"}\n```\n- Image:\n```json\n{\"id\": 0, \"image_id\": \"paper/page_0.jpg\", \"image_path\": \"images/paper/page_0.jpg\"}\n```\n---\n\n## `chunk_documents`\n\n**Signature**\n```python\n@app.tool(output=\"raw_chunk_path,chunk_backend_configs,chunk_backend,tokenizer_or_token_counter,chunk_size,chunk_path,use_title->None\")\nasync def chunk_documents(\n    raw_chunk_path: str,\n    chunk_backend_configs: Dict[str, Any],\n    chunk_backend: str = \"token\",\n    tokenizer_or_token_counter: str = \"character\",\n    chunk_size: int = 256,\n    chunk_path: Optional[str] = None,\n    use_title: bool = True,\n) -> None\n```\n\n**Function**\n- Chunks input text corpus (JSONL, containing `id/title/contents`) into paragraphs using selected backend:\n- Chunk Backend: Supports `token` / `sentence` / `recursive`.\n- Tokenizer: `tokenizer_or_token_counter` can be `word`, `character`, or `tiktoken` encoding name (e.g., `gpt2`).\n- Chunk Size: Controls block size via `chunk_size` (overlap defaults to size/4).\n- Optionally appends document title to the beginning of each block (`use_title`).\n\n**Output Format (JSONL)**\n```json\n{\"id\": 0, \"doc_id\": \"paper\", \"title\": \"paper\", \"contents\": \"Chunked text\"}\n```\n\n---\n\n## Configuration\n\n```yaml servers/corpus/parameter.yaml icon=\"/images/yaml.svg\"\n# servers/corpus/parameter.yaml\nparse_file_path: data/UltraRAG.pdf\ntext_corpus_save_path: corpora/text.jsonl\nimage_corpus_save_path: corpora/image.jsonl\n\n# mineru\nmineru_dir: corpora/\nmineru_extra_params:\n  source: modelscope\n\n# chunking parameters\nraw_chunk_path: corpora/text.jsonl\nchunk_path: corpora/chunks.jsonl\nuse_title: false\nchunk_backend: sentence # choices=[\"token\", \"sentence\", \"recursive\"]\ntokenizer_or_token_counter: character\nchunk_size: 512\nchunk_backend_configs:\n  token:\n    chunk_overlap: 50\n  sentence:\n    chunk_overlap: 50\n    min_sentences_per_chunk: 1\n    delim: \"['.', '!', '?', '；', '。', '！', '？', '\\\\n']\"\n  recursive:\n    min_characters_per_chunk: 12\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `parse_file_path` | str | Input file or directory path |\n| `text_corpus_save_path` | str | Text corpus output path (JSONL) |\n| `image_corpus_save_path` | str | Image corpus index output path (JSONL) |\n| `mineru_dir` | str | MinerU output root directory |\n| `mineru_extra_params` | dict | MinerU extra parameters, such as `source`, `layout`, etc. |\n| `raw_chunk_path` | str | Chunking input file path (JSONL format) |\n| `chunk_path` | str | Chunking output path |\n| `use_title` | bool | Whether to append document title to the beginning of each chunk |\n| `chunk_backend` | str | Select chunking method: `token`, `sentence`, `recursive` |\n| `tokenizer_or_token_counter` | str | Tokenizer or counting method. Options: `word`, `character` or `tiktoken` model name (e.g., `gpt2`) |\n| `chunk_size` | int | Target size for each chunk |\n| `chunk_backend_configs` | dict | Configuration items for each chunking method (see below) |\n\n`chunk_backend_configs` Detailed Parameters:\n\n| Backend Type | Parameter | Description |\n|---|---|---|\n| **token** | `chunk_overlap` | Overlapping tokens between chunks |\n| **sentence** | `chunk_overlap` | Overlapping count between chunks |\n|  | `min_sentences_per_chunk` | Minimum number of sentences per chunk |\n|  | `delim` | Sentence delimiter list (Python list in string format) |\n| **recursive** | `min_characters_per_chunk` | Minimum character unit for recursive splitting |\n\n================\nFile: pages/en/api/custom.mdx\n================\n---\ntitle: \"Custom\"\nicon: \"puzzle-piece\"\n---\n\n## Search-R1 Tools\n\n### `search_r1_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef search_r1_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts query content from model response.\n- **Logic**: Uses regex `r\"<search>([^<]*)\"` to extract the content inside the last `<search>` tag. If not found, returns \"There is no query.\"; if the query does not end with `?`, it is automatically completed.\n\n### `r1_searcher_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef r1_searcher_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts query from R1-Searcher response.\n- **Logic**: Uses regex `r\"<|begin_of_query|>([^<]*)\"` to extract the last tag content.\n\n---\n\n## IRCoT & IterRetGen Tools\n\n### `iterretgen_nextquery`\n```python\n@app.tool(output=\"q_ls,ret_psg->nextq_ls\")\ndef iterretgen_nextquery(q_ls: List[str], ans_ls: List[str | Any]) -> Dict[str, List[str]]\n```\n- **Function**: Iterative retrieval generation.\n- **Logic**: `next_query = f\"{q} {ans}\"`. Concatenates original question and generated answer as the Query for the next retrieval.\n\n### `ircot_get_first_sent`\n```python\n@app.tool(output=\"ans_ls->q_ls\")\ndef ircot_get_first_sent(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts the first sentence of the answer (up to period or question/exclamation mark).\n\n### `ircot_extract_ans`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef ircot_extract_ans(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts the final answer.\n- **Logic**: Matches content after `so the answer is [...]`.\n\n---\n\n## Search-o1 Tools\n\n### `search_o1_init_list`\n```python\n@app.tool(output=\"q_ls->total_subq_list,total_reason_list,total_final_info_list\")\ndef search_o1_init_list(q_ls: List[str]) -> Dict[str, List[Any]]\n```\n- **Function**: Initializes accumulation lists required by Search-o1 (sub-questions, reasoning, final info), initially filled with `<PAD>`.\n\n### `search_o1_combine_list`\n```python\n@app.tool(output=\"total_subq_list, extract_query_list, total_reason_list, extract_reason_list->total_subq_list, total_reason_list\")\ndef search_o1_combine_list(...)\n```\n- **Function**: Appends the extracted Query and Reasoning of the current step to the total lists.\n\n### `search_o1_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef search_o1_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts content between `<|begin_search_query|>...<|end_search_query|>`.\n\n### `search_o1_reasoning_extract`\n```python\n@app.tool(output=\"ans_ls->extract_reason_list\")\ndef search_o1_reasoning_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts all text before `<|begin_search_query|>` as the reasoning process.\n\n### `search_o1_extract_final_information`\n```python\n@app.tool(output=\"ans_ls->extract_final_infor_list\")\ndef search_o1_extract_final_information(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts content after `**Final Information**` marker.\n\n---\n\n## Utility Tools\n\n### `output_extract_from_boxed`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef output_extract_from_boxed(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts answer from LaTeX `\\boxed{...}`. Supports nested bracket handling and format cleaning.\n\n### `merge_passages`\n```python\n@app.tool(output=\"temp_psg,ret_psg->ret_psg\")\ndef merge_passages(temp_psg: List[str | Any], ret_psg: List[str | Any]) -> Dict[str, List[str | Any]]\n```\n- **Function**: Appends `temp_psg` list to `ret_psg` list.\n\n### `evisrag_output_extract_from_special`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef evisrag_output_extract_from_special(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts answer from `<answer>...</answer>` tags.\n\n### `assign_citation_ids` / `assign_citation_ids_stateful`\n- `assign_citation_ids`: Assigns citation IDs in the form of `[1]`, `[2]` to retrieved passages.\n- `assign_citation_ids_stateful`: Uses `CitationRegistry` class to maintain global citation IDs (cross-step deduplication).\n- `init_citation_registry`: Resets global citation registry.\n\n---\n\n## SurveyCPM Tools\n\n### `surveycpm_state_init`\n```python\n@app.tool(output=\"instruction_ls->state_ls,cursor_ls,survey_ls,step_ls,extend_time_ls,extend_result_ls,retrieved_info_ls,parsed_ls\")\ndef surveycpm_state_init(instruction_ls: List[str]) -> Dict[str, List]\n```\n- **Function**: Initializes SurveyCPM state machine.\n- **Initial State**: `state=\"search\"`, `cursor=\"outline\"`, `step=0`.\n\n### `surveycpm_parse_search_response`\n```python\n@app.tool(output=\"response_ls,surveycpm_hard_mode->keywords_ls,parsed_ls\")\ndef surveycpm_parse_search_response(response_ls: List[str], surveycpm_hard_mode: bool = True) -> Dict[str, List]\n```\n- **Function**: Parses search instructions (JSON or XML format) generated by the model, extracts keyword list.\n\n### `surveycpm_process_passages`\n```python\n@app.tool(output=\"ret_psg_ls->retrieved_info_ls\")\ndef surveycpm_process_passages(ret_psg_ls: List[List[List[str]]]) -> Dict[str, List[str]]\n```\n- **Function**: Processes retrieved passages, deduplicates, limits quantity (Top-K), and concatenates into string.\n\n### `surveycpm_after_init_plan` / `after_write` / `after_extend`\n- **Function**: Parses Agent response for different stages (initialize outline, write content, extend plan).\n- **Logic**:\n  - Calls `surveycpm_parse_response` to validate format and content.\n  - Updates `survey_ls` (outline structure) and `cursor_ls` (current cursor position) if successful.\n  - Keeps original state for retry if failed.\n\n### `surveycpm_update_state`\n```python\n@app.tool(output=\"state_ls,cursor_ls,extend_time_ls,extend_result_ls,step_ls,parsed_ls,surveycpm_max_step,surveycpm_max_extend_step->state_ls,extend_time_ls,step_ls\")\ndef surveycpm_update_state(...)\n```\n- **Function**: Core state machine logic.\n- **State Transition**:\n  - `search` -> `analyst-init_plan` (cursor=\"outline\")\n  - `search` -> `write` (cursor=section-X)\n  - `write` -> `search` (continue writing) or `analyst-extend_plan` (finished current section)\n  - `analyst-extend_plan` -> `search` (extend success) or `done` (no extension)\n  - Exceeds max steps -> `done`\n\n### `surveycpm_format_output`\n```python\n@app.tool(output=\"survey_ls,instruction_ls->ans_ls\")\ndef surveycpm_format_output(survey_ls: List[str], instruction_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Converts final Survey JSON to Markdown format.\n- **Processing**: Automatically handles heading levels (# ## ###), citation formatting (`\\cite{...}` to `[1]`), and text cleaning.\n\n---\n\n## Configuration\n\n```yaml servers/custom/parameter.yaml icon=\"/images/yaml.svg\"\nsurveycpm_hard_mode: false\nsurveycpm_max_step: 140\nsurveycpm_max_extend_step: 12\n```\n\n| Parameter | Type | Description |\n|---|---|---|\n| `surveycpm_hard_mode` | bool | Whether to enable SurveyCPM's strict parsing mode (validate JSON field integrity) |\n| `surveycpm_max_step` | int | Maximum total execution steps, forced end if exceeded |\n| `surveycpm_max_extend_step` | int | Maximum plan extension times |\n\n================\nFile: pages/en/api/evaluation.mdx\n================\n---\ntitle: \"Evaluation\"\nicon: \"clipboard-check\"\n---\n\n## `evaluate`\n\n**Signature**\n```python\n@app.tool(output=\"pred_ls,gt_ls,metrics,save_path->eval_res\")\ndef evaluate(\n    pred_ls: List[str],\n    gt_ls: List[List[str]],\n    metrics: List[str] | None,\n    save_path: str,\n) -> Dict[str, Any]\n```\n\n**Function**\n- Executes automatic metric evaluation for QA/Generation tasks.\n- Supported Metrics: `acc`, `em`, `coverem`, `stringem`, `f1`, `rouge-1`, `rouge-2`, `rouge-l`.\n- Results are automatically saved as `.json` file and printed as Markdown table.\n\n\n---\n\n## `evaluate_trec`\n\n**Signature**\n```python\n@app.tool(output=\"run_path,qrels_path,ir_metrics,ks,save_path->eval_res\")\ndef evaluate_trec(\n    run_path: str,\n    qrels_path: str,\n    metrics: List[str] | None,\n    ks: List[int] | None,\n    save_path: str,\n)\n```\n\n**Function**\n- Performs IR retrieval metric evaluation based on `pytrec_eval`.\n- Reads standard TREC format:\n  - **qrels**: `<qid> <iter> <docid> <rel>`\n  - **run**: `<qid> Q0 <docid> <rank> <score> <tag>`\n- Supported Metrics: `mrr`, `map`, `recall@k`, `precision@k`, `ndcg@k`.\n- Automatically aggregates statistics and outputs as table.\n\n---\n\n## `evaluate_trec_pvalue`\n\n**Signature**\n```python\n@app.tool(\n    output=\"run_new_path,run_old_path,qrels_path,ir_metrics,ks,n_resamples,save_path->eval_res\"\n)\ndef evaluate_trec_pvalue(\n    run_new_path: str,\n    run_old_path: str,\n    qrels_path: str,\n    metrics: List[str] | None,\n    ks: List[int] | None,\n    n_resamples: int | None,\n    save_path: str,\n)\n```\n\n**Function**\n- Compares significance of two TREC result files using **Two-sided Permutation Test** to calculate p-value.\n- Default resampling count `n_resamples=10000`.\n- Outputs mean, difference, p-value, and significance flag.\n\n\n---\n\n## Configuration\n\n```yaml servers/evaluation/parameter.yaml icon=\"/images/yaml.svg\"\nsave_path: output/evaluate_results.json\n\n# QA task\nmetrics: [ 'acc', 'f1', 'em', 'coverem', 'stringem', 'rouge-1', 'rouge-2', 'rouge-l' ]\n\n# Retrieval task\nqrels_path: data/qrels.txt\nrun_path: data/run_a.txt\nks: [ 1, 5, 10, 20, 50, 100 ]\nir_metrics: [ \"mrr\", \"map\", \"recall\", \"ndcg\", \"precision\" ]\n\n# significant\nrun_new_path: data/run_a.txt\nrun_old_path: data/run_b.txt\nn_resamples: 10000\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `save_path` | str | Evaluation result save path (automatically appends timestamp) |\n| `metrics` | list[str] | Metric set used for QA / Generation tasks |\n| `qrels_path` | str | TREC format ground truth file path |\n| `run_path` | str | Result file for retrieval task |\n| `ks` | list[int] | Truncation levels for calculating NDCG@K, P@K, Recall@K, etc. |\n| `ir_metrics` | list[str] | Retrieval task metric names, supports `mrr`, `map`, `recall`, `ndcg`, `precision` |\n| `run_new_path` | str | Run file path generated by new model (significance analysis) |\n| `run_old_path` | str | Run file path of old model (significance analysis) |\n| `n_resamples` | int | Resampling count for Permutation Test |\n\n================\nFile: pages/en/api/generation.mdx\n================\n---\ntitle: \"Generation\"\nicon: \"pen-nib\"\n---\n\n## `generation_init`\n\n**Signature**\n```python\ndef generation_init(\n    backend_configs: Dict[str, Any],\n    sampling_params: Dict[str, Any],\n    extra_params: Optional[Dict[str, Any]] = None,\n    backend: str = \"vllm\",\n) -> None\n```\n\n**Function**\n- Initializes inference backend and sampling parameters.\n- Supports `vllm`, `openai`, `hf` backends.\n- `extra_params` can be used to pass `chat_template_kwargs` or other backend-specific parameters.\n\n---\n\n## `generate`\n\n**Signature**\n```python\nasync def generate(\n    prompt_ls: List[Union[str, Dict[str, Any]]],\n    system_prompt: str = \"\",\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Plain text conversation generation.\n- Automatically handles Prompt in list, supports string or OpenAI format dictionary.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"answer for prompt_0\", \"answer for prompt_1\", \"...\"]}\n```\n\n---\n\n## `multimodal_generate`\n\n**Signature**\n```python\nasync def multimodal_generate(\n    multimodal_path: List[List[str]],\n    prompt_ls: List[Union[str, Dict[str, Any]]],\n    system_prompt: str = \"\",\n    image_tag: Optional[str] = None,\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Text-image multimodal conversation generation.\n- `multimodal_path`: List of image paths corresponding to each Prompt (supports local path or URL).\n- `image_tag`: If specified (e.g., `<img>`), inserts image at that tag's position in Prompt; otherwise defaults to appending to end of Prompt.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"answer with images for prompt_0\", \"...\"]}\n```\n\n---\n\n## `multiturn_generate`\n\n**Signature**\n```python\nasync def multiturn_generate(\n    messages: List[Dict[str, str]],\n    system_prompt: str = \"\",\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Multi-turn conversation generation.\n- Supports only single-call generation, does not handle batch Prompts.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"assistant response\"]}\n```\n\n---\n\n## `vllm_shutdown`\n\n**Signature**\n```python\ndef vllm_shutdown() -> None\n```\n\n**Function**\n- Explicitly shuts down vLLM engine and releases VRAM resources.\n- Valid only when using `vllm` backend.\n\n---\n\n## Configuration\n\n```yaml servers/generation/parameter.yaml icon=\"/images/yaml.svg\"\n# servers/generation/parameter.yaml\nbackend: vllm # options: vllm, openai\nbackend_configs:\n  vllm:\n    model_name_or_path: openbmb/MiniCPM4-8B\n    gpu_ids: \"2,3\"\n    gpu_memory_utilization: 0.9\n    dtype: auto\n    trust_remote_code: true\n  openai:\n    model_name: MiniCPM4-8B\n    base_url: http://localhost:8000/v1\n    api_key: \"abc\"\n    concurrency: 8\n    retries: 3\n    base_delay: 1.0\n  hf:\n    model_name_or_path: openbmb/MiniCPM4-8B\n    gpu_ids: '2,3'\n    trust_remote_code: true\n    batch_size: 8\nsampling_params:\n  temperature: 0.7\n  top_p: 0.8\n  max_tokens: 2048\nextra_params:\n  chat_template_kwargs:\n    enable_thinking: false\nsystem_prompt: \"\"\nimage_tag: null\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `backend` | str | Specify generation backend, options `vllm`, `openai`, or `hf` (Transformers) |\n| `backend_configs` | dict | Model and runtime environment configuration for each backend |\n| `sampling_params` | dict | Sampling parameters to control generation diversity and length |\n| `extra_params` | dict | Extra parameters, e.g., `chat_template_kwargs` |\n| `system_prompt` | str | Global system prompt, added to context as `system` message |\n| `image_tag` | str | Image placeholder tag (if needed) |\n\n`backend_configs` Detailed Description:\n\n| Backend | Parameter | Description |\n|---|---|---|\n| **vllm** | `model_name_or_path` | Model name or path |\n|  | `gpu_ids` | GPU IDs used (e.g., `\"0,1\"`) |\n|  | `gpu_memory_utilization` | GPU memory utilization ratio (0–1) |\n|  | `dtype` | Data type (e.g., `auto`, `bfloat16`) |\n|  | `trust_remote_code` | Whether to trust remote code |\n| **openai** | `model_name` | OpenAI model name or self-hosted compatible model |\n|  | `base_url` | API base URL |\n|  | `api_key` | API Key |\n|  | `concurrency` | Max concurrent requests |\n|  | `retries` | API retry count |\n|  | `base_delay` | Base wait time for each retry (seconds) |\n| **hf** | `model_name_or_path` | Transformers model path |\n|  | `gpu_ids` | GPU IDs (same as above) |\n|  | `trust_remote_code` | Whether to trust remote code |\n|  | `batch_size` | Batch size per inference |\n\n`sampling_params` Detailed Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `temperature` | float | Controls randomness, higher means more diverse generation |\n| `top_p` | float | Nucleus sampling threshold |\n| `max_tokens` | int | Max generated tokens |\n\n================\nFile: pages/en/api/prompt.mdx\n================\n---\ntitle: \"Prompt\"\nicon: \"terminal\"\n---\n\n## QA Prompts\n\n### `qa_boxed`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,template->prompt_ls\")\ndef qa_boxed(\n    q_ls: List[str], \n    template: str | Path\n) -> List[PromptMessage]\n```\n\n**Function**\n\nBasic Q&A Prompt.\n\nLoads specified Jinja2 template, renders each question in the question list into a Prompt.\n\nTemplate Variable: `{{ question }}`\n\n### `qa_boxed_multiple_choice`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,choices_ls,template->prompt_ls\")\ndef qa_boxed_multiple_choice(\n    q_ls: List[str],\n    choices_ls: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nMultiple Choice Q&A Prompt.\n\nAutomatically formats choice list into \"A: ..., B: ...\" form and injects into template.\n\nTemplate Variables: `{{ question }}`, `{{ choices }}`\n\n### `qa_rag_boxed`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,template->prompt_ls\")\ndef qa_rag_boxed(\n    q_ls: List[str], \n    ret_psg: List[str | Any], \n    template: str | Path\n) -> list[PromptMessage]\n```\n\n**Function**\n\nStandard RAG Prompt.\n\nConcatenates retrieved passage lists and injects into template.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `qa_rag_boxed_multiple_choice`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,choices_ls,ret_psg,template->prompt_ls\")\ndef qa_rag_boxed_multiple_choice(\n    q_ls: List[str],\n    choices_ls: List[List[str]],\n    ret_psg: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nMultiple Choice Q&A Prompt with Retrieval Context.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`, `{{ choices }}`\n\n---\n\n## RankCoT Prompts\n\n### `RankCoT_kr`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,kr_template->prompt_ls\")\ndef RankCoT_kr(\n    q_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> list[PromptMessage]\n```\n\n**Function**\n\nRankCoT Phase 1: Knowledge Retrieval Prompt.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `RankCoT_qa`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,kr_ls,qa_template->prompt_ls\")\ndef RankCoT_qa(\n    q_ls: List[str],\n    kr_ls: List[str],\n    template: str | Path,\n) -> list[PromptMessage]\n```\n\n**Function**\n\nRankCoT Phase 2: Chain-of-Thought based Q&A Prompt.\n\nTemplate Variables: `{{ question }}`, `{{ CoT }}` (Here CoT is usually knowledge generated in the previous phase)\n\n---\n\n## IRCoT Prompts\n\n### `ircot_next_prompt`\n\n**Signature**\n\n```python\n@app.prompt(output=\"memory_q_ls,memory_ret_psg,template->prompt_ls\")\ndef ircot_next_prompt(\n    memory_q_ls: List[List[str | None]],\n    memory_ret_psg: List[List[List[str]] | None],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nIRCoT (Interleaved Retrieval CoT) Iterative Prompt Generation.\n\nConstructs next round's Prompt based on historical retrieval results and chain of thought. Supports single-turn and multi-turn history concatenation.\n\nTemplate Variables: `{{ documents }}`, `{{ question }}`, `{{ cur_answer }}`\n\n---\n\n## WebNote Prompts\n\n### `webnote_init_page`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,webnote_init_page_template->prompt_ls\")\ndef webnote_init_page(\n    q_ls: List[str],\n    plan_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Initialize note page.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`\n\n### `webnote_gen_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,webnote_gen_plan_template->prompt_ls\")\ndef webnote_gen_plan(\n    q_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate search plan.\n\nTemplate Variable: `{{ question }}`\n\n### `webnote_gen_subq`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,page_ls,webnote_gen_subq_template->prompt_ls\")\ndef webnote_gen_subq(\n    q_ls: List[str],\n    plan_ls: List[str],\n    page_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate sub-questions.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`, `{{ page }}`\n\n### `webnote_fill_page`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,page_ls,subq_ls,psg_ls,webnote_fill_page_template->prompt_ls\")\ndef webnote_fill_page(\n    q_ls: List[str],\n    plan_ls: List[str],\n    page_ls: List[str],\n    subq_ls: List[str],\n    psg_ls: List[Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Fill notes based on retrieval results.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`, `{{ sub_question }}`, `{{ docs_text }}`, `{{ page }}`\n\n### `webnote_gen_answer`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,page_ls,webnote_gen_answer_template->prompt_ls\")\ndef webnote_gen_answer(\n    q_ls: List[str],\n    page_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate final answer based on notes.\n\nTemplate Variables: `{{ question }}`, `{{ page }}`\n\n---\n\n## Search-R1 & R1-Searcher\n\n### `search_r1_gen`\n\n**Signature**\n\n```python\n@app.prompt(output=\"prompt_ls,ans_ls,ret_psg,search_r1_gen_template->prompt_ls\")\ndef search_r1_gen(\n    prompt_ls: List[PromptMessage],\n    ans_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nGeneration Prompt suitable for R1 style.\n\nTruncates Top-3 retrieval passages and injects into context.\n\nTemplate Variables: `{{ history }}`, `{{ answer }}`, `{{ passages }}`\n\n### `r1_searcher_gen`\n\n**Signature**\n\n```python\n@app.prompt(output=\"prompt_ls,ans_ls,ret_psg,r1_searcher_gen_template->prompt_ls\")\ndef r1_searcher_gen(\n    prompt_ls: List[PromptMessage],\n    ans_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nGeneration Prompt suitable for R1 Searcher.\n\nTruncates Top-5 retrieval passages.\n\nTemplate Variables: `{{ history }}`, `{{ answer }}`, `{{ passages }}`\n\n---\n\n## Search-o1 Prompts\n\n### `search_o1_init`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,searcho1_reasoning_template->prompt_ls\")\ndef search_o1_init(\n    q_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Initial Reasoning Prompt.\n\nTemplate Variable: `{{ question }}`\n\n### `search_o1_reasoning_indocument`\n\n**Signature**\n\n```python\n@app.prompt(output=\"extract_query_list,ret_psg,total_reason_list,searcho1_refine_template->prompt_ls\")\ndef search_o1_reasoning_indocument(\n    extract_query_list: List[str], \n    ret_psg: List[List[str]],       \n    total_reason_list: List[List[str]], \n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Reasoning Refinement Prompt.\n\nMerges historical reasoning steps (first + last 3 steps) with current retrieved documents for next step reasoning.\n\nTemplate Variables: `{{ prev_reasoning }}`, `{{ search_query }}`, `{{ document }}`\n\n### `search_o1_insert`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,total_subq_list,total_final_info_list,searcho1_reasoning_template->prompt_ls\") \ndef search_o1_insert(\n    q_ls: List[str],\n    total_subq_list: List[List[str]], \n    total_final_info_list: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Formatted Insertion Prompt.\n\nExplicitly inserts `<|begin_search_query|>` and search result tags into Prompt to construct complete chain-of-thought context.\n\n---\n\n## EVisRAG & Multi-branch Prompts\n\n### `gen_subq`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,gen_subq_template->prompt_ls\")\ndef gen_subq(\n    q_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nLoop/Branch Demo: Generate sub-questions based on documents.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `evisrag_vqa`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,evisrag_template->prompt_ls\")\ndef evisrag_vqa(\n    q_ls: List[str], \n    ret_psg: List[str | Any], \n    template: str | Path\n) -> list[PromptMessage]\n```\n\n**Function**\n\nMultimodal VQA RAG Prompt.\n\nAutomatically repeatedly inserts `<image>` Token into Prompt according to the number of retrieved images.\n\nTemplate Variable: `{{ question }}` (contains automatically injected image tokens)\n\n---\n\n## SurveyCPM Prompts\n\n### `surveycpm_search`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,cursor_ls,surveycpm_search_template->prompt_ls\")\ndef surveycpm_search(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    cursor_ls: List[str | None],\n    surveycpm_search_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Decide next search content.\n\nParses JSON format outline, generates text description of current outline.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_outline }}`, `{{ current_instruction }}`\n\n### `surveycpm_init_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,retrieved_info_ls,surveycpm_init_plan_template->prompt_ls\")\ndef surveycpm_init_plan(\n    instruction_ls: List[str],\n    retrieved_info_ls: List[str],\n    surveycpm_init_plan_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Initialize outline plan.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_information }}`\n\n### `surveycpm_write`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,cursor_ls,retrieved_info_ls,surveycpm_write_template->prompt_ls\")\ndef surveycpm_write(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    cursor_ls: List[str | None],\n    retrieved_info_ls: List[str],\n    surveycpm_write_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Write specific section content.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_survey }}`, `{{ current_instruction }}`, `{{ current_information }}`\n\n### `surveycpm_extend_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,surveycpm_extend_plan_template->prompt_ls\")\ndef surveycpm_extend_plan(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    surveycpm_extend_plan_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Extend or modify outline plan.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_survey }}`\n\n---\n\n## Configuration\n\n```yaml servers/prompt/parameter.yaml icon=\"/images/yaml.svg\"\n# QA\ntemplate: prompt/qa_boxed.jinja\n\n# RankCoT\nkr_template: prompt/RankCoT_knowledge_refinement.jinja\nqa_template: prompt/RankCoT_question_answering.jinja\n\n# Search-R1\nsearch_r1_gen_template: prompt/search_r1_append.jinja\n\n# R1-Searcher\nr1_searcher_gen_template: prompt/r1_searcher_append.jinja\n\n# Search-o1\nsearcho1_reasoning_template: prompt/search_o1_reasoning.jinja\nsearcho1_refine_template: prompt/search_o1_refinement.jinja\n\n\n# For other prompts, please add parameters here as needed\n\n# Take webnote as an example:\nwebnote_gen_plan_template: prompt/webnote_gen_plan.jinja\nwebnote_init_page_template: prompt/webnote_init_page.jinja\nwebnote_gen_subq_template: prompt/webnote_gen_subq.jinja\nwebnote_fill_page_template: prompt/webnote_fill_page.jinja\nwebnote_gen_answer_template: prompt/webnote_gen_answer.jinja\n\n# SurveyCPM\nsurveycpm_search_template: prompt/surveycpm_search.jinja\nsurveycpm_init_plan_template: prompt/surveycpm_init_plan.jinja\nsurveycpm_write_template: prompt/surveycpm_write.jinja\nsurveycpm_extend_plan_template: prompt/surveycpm_extend_plan.jinja\n```\n\n| Parameter | Description |\n|---|---|\n| `template` | Basic QA template path |\n| `kr_template` | RankCoT knowledge refinement template path |\n| `qa_template` | RankCoT Q&A template path |\n| `*_template` | Jinja2 template file path corresponding to each module function |\n\n================\nFile: pages/en/api/reranker.mdx\n================\n---\ntitle: \"Reranker\"\nicon: \"ranking-star\"\n---\n\n## `reranker_init`\n\n**Signature**\n```python\nasync def reranker_init(\n    model_name_or_path: str,\n    backend_configs: Dict[str, Any],\n    batch_size: int,\n    gpu_ids: Optional[object] = None,\n    backend: str = \"infinity\",\n) -> None\n```\n\n**Function**\n- Initializes reranker backend and model.\n\n---\n\n## `reranker_rerank`\n\n**Signature**\n```python\nasync def reranker_rerank(\n    query_list: List[str],\n    passages_list: List[List[str]],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n) -> Dict[str, List[Any]]\n```\n\n**Function**\n- **Reranks** candidate passages:\n\n**Output Format (JSON)**\n```json\n{\n  \"rerank_psg\": [\n    [\"best passage for q0\", \"...\"],\n    [\"best passage for q1\", \"...\"]\n  ]\n}\n```\n\n---\n\n## Configuration\n\n```yaml servers/reranker/parameter.yaml icon=\"/images/yaml.svg\"\nmodel_name_or_path: openbmb/MiniCPM-Reranker-Light\nbackend: sentence_transformers # options: infinity, sentence_transformers, openai\nbackend_configs:\n  infinity:\n    bettertransformer: false\n    pooling_method: auto\n    device: cuda\n    model_warmup: false\n    trust_remote_code: true\n  sentence_transformers:\n    device: cuda\n    trust_remote_code: true\n  openai:\n    model_name: text-embedding-3-small\n    base_url: \"https://api.openai.com/v1\"\n    api_key: \"\"\n\ngpu_ids: 0\ntop_k: 5\nbatch_size: 16\nquery_instruction: \"\"\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `model_name_or_path` | str | Model path or name (local or HuggingFace repo) |\n| `backend` | str | Select backend type: `infinity`, `sentence_transformers` or `openai` |\n| `backend_configs` | dict | Exclusive parameter settings for each backend |\n| `gpu_ids` | str/int | Specify GPU ID (can be multi-card, e.g., `\"0,1\"`) |\n| `top_k` | int | Number of reranked results returned |\n| `batch_size` | int | Sample quantity per batch |\n| `query_instruction` | str | Query prefix hint, used for prompt engineering or query modification |\n\n`backend_configs` Detailed Description:\n\n| Backend | Parameter | Description |\n|---|---|---|\n| **infinity** | `device` | Device type (cuda / cpu) |\n|  | `bettertransformer` | Whether to enable accelerated inference |\n|  | `pooling_method` | Vector pooling strategy |\n|  | `model_warmup` | Whether to warmup model |\n|  | `trust_remote_code` | Whether to trust remote code (Required for HuggingFace models) |\n| **sentence_transformers** | `device` | Device type (cuda / cpu) |\n|  | `trust_remote_code` | Whether to trust remote code |\n| **openai** | `model_name` | API Model name |\n|  | `base_url` | API access address |\n|  | `api_key` | OpenAI API Key |\n\n================\nFile: pages/en/api/retriever.mdx\n================\n---\ntitle: \"Retriever\"\nicon: \"magnifying-glass\"\n---\n\n## `retriever_init`\n\n**Signature**\n```python\nasync def retriever_init(\n    model_name_or_path: str,\n    backend_configs: Dict[str, Any],\n    batch_size: int,\n    corpus_path: str,\n    gpu_ids: Optional[object] = None,\n    is_multimodal: bool = False,\n    backend: str = \"sentence_transformers\",\n    index_backend: str = \"faiss\",\n    index_backend_configs: Optional[Dict[str, Any]] = None,\n    is_demo: bool = False,\n    collection_name: str = \"\",\n) -> None\n```\n\n**Function**\n- Initializes retrieval service.\n- Embedding Backend (backend): Responsible for converting text/images into vectors (Infinity, SentenceTransformers, OpenAI, BM25).\n- Index Backend (index_backend): Responsible for vector storage and retrieval (FAISS, Milvus, Qdrant).\n- Demo Mode: If is_demo=True, forces OpenAI + Milvus configuration, ignoring some parameters.\n\n---\n\n## `retriever_embed`\n\n**Signature**\n```python\nasync def retriever_embed(\n    embedding_path: Optional[str] = None,\n    overwrite: bool = False,\n    is_multimodal: bool = False,\n) -> None\n```\n\n**Function**\n- (Non-Demo Mode) Batches calculation of vector representations of corpus and saves as .npy file.\n- Only applies to Dense Retriever backend (BM25 not supported).\n\n\n---\n\n## `retriever_index`\n\n**Signature**\n```python\nasync def retriever_index(\n    embedding_path: str,\n    overwrite: bool = False,\n    collection_name: str = \"\",\n    corpus_path: str = \"\"\n) -> None\n```\n\n**Function**\n- Builds retrieval index.\n- FAISS: Reads embedding_path (.npy) to build local index file.\n- Milvus / Qdrant / Demo: Reads corpus_path (.jsonl), generates vectors and inserts into specified collection_name.\n\n---\n\n## `retriever_search`\n\n**Signature**\n```python\nasync def retriever_search(\n    query_list: List[str],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n    collection_name: str = \"\",\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Retrieves single or multiple queries.\n- Automatically handles query vectorization (adds query_instruction) and finds Top-K in specified collection_name (for Milvus or Qdrant) or default index.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_batch_search`\n\n**Signature**\n```python\nasync def retriever_batch_search(\n    batch_query_list: List[List[str]],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n    collection_name: str = \"\",\n) -> Dict[str, List[List[List[str]]]]\n```\n\n**Function**\n- Batch version of retriever_search, accepts nested list input.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg_ls\": [[[\"psg 1-1\"], [\"psg 1-2\"]], [[\"psg 2-1\"]]]}\n```\n\n---\n\n## `bm25_index`\n\n**Signature**\n```python\nasync def bm25_index(\n    overwrite: bool = False,\n) -> None\n```\n\n**Function**\n- When `backend=\"bm25\"`, builds BM25 sparse index and saves it.\n\n---\n\n## `bm25_search`\n\n**Signature**\n```python\nasync def bm25_search(\n    query_list: List[str],\n    top_k: int = 5,\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Keyword retrieval based on BM25 algorithm.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_deploy_search`\n\n**Signature**\n```python\nasync def retriever_deploy_search(\n    retriever_url: str,\n    query_list: List[str],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- As a client, calls remote retrieval service deployed at retriever_url for query.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_websearch`\n\n**Signature**\n```python\nasync def retriever_websearch(\n    query_list: List[str],\n    top_k: Optional[int] | None = 5,\n    retrieve_thread_num: Optional[int] | None = 1,\n    websearch_backend: str = \"tavily\",\n    websearch_backend_configs: Optional[Dict[str, Any]] | None = None,\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Unified Web retrieval tool. Choose backend via `websearch_backend` (`tavily`, `exa`, `zhipuai`) and configure in `websearch_backend_configs`.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"snippet 1\", \"snippet 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_batch_websearch`\n\n**Signature**\n```python\nasync def retriever_batch_websearch(\n    batch_query_list: List[List[str]],\n    top_k: Optional[int] | None = 5,\n    retrieve_thread_num: Optional[int] | None = 1,\n    websearch_backend: str = \"tavily\",\n    websearch_backend_configs: Optional[Dict[str, Any]] | None = None,\n) -> Dict[str, List[List[List[str]]]]\n```\n\n**Function**\n- Batch Web retrieval for SurveyCPM-style pipelines.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg_ls\": [[[\"snippet 1\", \"snippet 2\"]], [\"...\" ]]} \n```\n\n\n---\n\n## Configuration\n\n```yaml servers/retriever/parameter.yaml icon=\"/images/yaml.svg\"\nmodel_name_or_path: openbmb/MiniCPM-Embedding-Light\ncorpus_path: data/corpus_example.jsonl\nembedding_path: embedding/embedding.npy\ncollection_name: wiki\n\n# Embedding Backend Configuration\nbackend: sentence_transformers # options: infinity, sentence_transformers, openai, bm25\nbackend_configs:\n  infinity:\n    bettertransformer: false\n    pooling_method: auto\n    model_warmup: false\n    trust_remote_code: true\n  sentence_transformers:\n    trust_remote_code: true\n    sentence_transformers_encode:\n      normalize_embeddings: false\n      encode_chunk_size: 256\n      q_prompt_name: query\n      psg_prompt_name: document\n      psg_task: null\n      q_task: null\n  openai:\n    model_name: text-embedding-3-small\n    base_url: \"https://api.openai.com/v1\"\n    api_key: \"abc\"\n  bm25:\n    lang: en\n    save_path: index/bm25\n\n# Index Backend Configuration\nindex_backend: faiss # options: faiss, milvus, qdrant\nindex_backend_configs:\n  faiss:\n    index_use_gpu: True\n    index_chunk_size: 10000\n    index_path: index/index.index\n  milvus:\n    uri: index/milvus_demo.db # Local file for Lite, or http://host:port\n    token: null\n    id_field_name: id\n    vector_field_name: vector\n    text_field_name: contents\n    index_params:\n      index_type: AUTOINDEX\n      metric_type: IP\n\n# Websearch Backend Configuration\nwebsearch_backend: tavily # options: tavily, exa, zhipuai\nwebsearch_backend_configs:\n  exa:\n    api_key: \"\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n  tavily:\n    api_key: \"\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n  zhipuai:\n    api_key: \"\"\n    base_url: \"https://open.bigmodel.cn/api/paas/v4/web_search\"\n    search_engine: \"search_std\"\n    search_intent: false\n    search_recency_filter: \"noLimit\"\n    content_size: \"medium\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n\nbatch_size: 16\ntop_k: 5\ngpu_ids: \"1\"\nquery_instruction: \"\"\nis_multimodal: false\noverwrite: false\nretrieve_thread_num: 1\nretriever_url: \"http://127.0.0.1:64501\"\nis_demo: false\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `model_name_or_path` | str | Retrieval model path or name (e.g., HuggingFace model ID) |\n| `corpus_path` | str | Input corpus JSONL file path |\n| `embedding_path` | str | Vector file save path (`.npy`) |\n| `collection_name` | str | Milvus or Qdrant collection name |\n| `backend` | str | Select retrieval backend: `infinity`, `sentence_transformers`, `openai`, `bm25` |\n| `index_backend` | str | Index backend: `faiss`, `milvus`, `qdrant` |\n| `backend_configs` | dict | Parameter configuration for each backend (see table below) |\n| `index_backend_configs` | dict | Parameter configuration for each index backend (see table below) |\n| `websearch_backend` | str | Web search backend: `tavily`, `exa`, `zhipuai` |\n| `websearch_backend_configs` | dict | Web search backend configs (api_key, retries, payload, etc.) |\n| `batch_size` | int | Batch size for vector generation or retrieval |\n| `top_k` | int | Number of returned candidate passages |\n| `gpu_ids` | str | Specify visible GPU devices, e.g., `\"0,1\"` |\n| `query_instruction` | str | Query prefix (used by instruction-tuning models) |\n| `is_multimodal` | bool | Whether to enable multimodal embedding (e.g., image) |\n| `overwrite` | bool | Whether to overwrite if embedding or index file already exists |\n| `retrieve_thread_num` | int | Concurrent thread number for Web retrieval |\n| `retriever_url` | str | URL of deployed retriever server |\n| `is_demo` | bool | Demo mode s","CLAUDE.md":"# AGENTS.md\n\nThis document is the primary engineering guide for autonomous coding agents working in the `UltraRAG` repository.\n\nUse this file as the source of truth for architecture, conventions, workflows, and safe change patterns.\nIf `CLAUDE.md` exists, it should only point to this file.\n\n---\n\n## 1) Project Identity\n\n`UltraRAG` is a lightweight RAG framework built around the Model Context Protocol (MCP).\nThe key design choice is strict modularization: retrieval, prompting, generation, routing, memory, and evaluation are implemented as independent MCP servers orchestrated by YAML pipelines.\n\nCurrent core metadata:\n\n- Package: `ultrarag`\n- Version: `0.3.0`\n- Python: `>=3.11, <3.13`\n- CLI entrypoint: `ultrarag = ultrarag.client:main`\n- Package manager: `uv` (`[tool.uv] package = true`)\n\n---\n\n## 2) Repository Map (What Matters Most)\n\n```text\nUltraRAG/\n├── src/ultrarag/                    # Installable core package\n│   ├── client.py                    # CLI + pipeline engine + run/build orchestration\n│   ├── server.py                    # UltraRAG_MCP_Server (FastMCP extension)\n│   ├── api.py                       # Python API wrappers (ToolCall, PipelineCall)\n│   ├── cli.py                       # Rich banner and CLI visuals\n│   ├── mcp_logging.py               # Central logging setup\n│   ├── mcp_exceptions.py            # Node.js checks for remote MCP\n│   └── utils.py                     # Subprocess lifecycle helpers\n│\n├── servers/                         # MCP microservices (each server is independent)\n│   ├── retriever/\n│   ├── generation/\n│   ├── prompt/\n│   ├── reranker/\n│   ├── benchmark/\n│   ├── evaluation/\n│   ├── corpus/\n│   ├── memory/\n│   ├── router/\n│   ├── custom/\n│   ├── pageindex/\n│   └── sayhello/\n│\n├── examples/\n│   ├── demos/                       # UI-ready demo pipelines\n│   └── experiments/                 # Experiment/research pipelines\n│\n├── ui/\n│   ├── backend/                     # Flask backend + pipeline manager\n│   └── frontend/                    # Vite + React + TypeScript\n│\n├── docs/                            # Docs and assets\n├── script/                          # Utility scripts (deploy, case study, etc.)\n├── pyproject.toml                   # Dependencies + package metadata\n├── uv.lock                          # Locked dependency graph\n├── Dockerfile*                      # Container variants\n└── .gitignore\n```\n\nImportant generated/derived files:\n\n- `servers/*/server.yaml` (generated by each server `build` tool)\n- `examples/**/parameter/*_parameter.yaml` (pipeline-merged parameters)\n- `examples/**/server/*_server.yaml` (pipeline-merged server config)\n- `output/memory_*.json` (per-run memory snapshots)\n\n---\n\n## 3) Mental Model of the System\n\nThink of UltraRAG as a three-layer system:\n\n1. **Interface layer**: CLI (`ultrarag ...`), UI (`ultrarag show ui`), and Python API (`ToolCall`, `PipelineCall`)\n2. **Orchestration layer**: `src/ultrarag/client.py` (`build`, `load_pipeline_context`, `execute_pipeline`)\n3. **Execution layer**: MCP servers in `servers/*`, each exposing tools/prompts over stdio (or remote MCP proxy)\n\nThe runtime contract is:\n\n- A pipeline YAML declares **which servers** to use and **which steps** to execute.\n- The client resolves I/O dependencies between steps.\n- Each step calls exactly one MCP tool or prompt.\n- Outputs are saved to a shared variable pool and can feed downstream steps.\n\n---\n\n## 4) Two-Phase Execution Lifecycle\n\n### Phase A: Build\n\nCommand:\n\n```bash\nultrarag build <pipeline.yaml>\n```\n\nWhat happens:\n\n- Reads `servers:` from the pipeline YAML.\n- For each referenced server, calls the server's `build` tool.\n- Produces:\n  - `<pipeline_dir>/parameter/<pipeline_name>_parameter.yaml`\n  - `<pipeline_dir>/server/<pipeline_name>_server.yaml`\n\nWhy this matters:\n\n- `build` materializes exact tool/prompt I/O metadata before runtime.\n- UI and runner rely on these generated artifacts for deterministic execution.\n\n### Phase B: Run\n\nCommand:\n\n```bash\nultrarag run <pipeline.yaml> [--param path] [--is_demo]\n```\n\nWhat happens:\n\n- Loads generated server config + parameter config.\n- Creates `fastmcp.Client` transport config for each server.\n- Executes pipeline steps in order, including `loop` and `branch`.\n- Saves intermediate memory snapshots and writes `output/memory_*.json`.\n- Invokes cleanup tools (e.g., tools ending with `vllm_shutdown`) if present.\n\n---\n\n## 5) Pipeline DSL Reference\n\nUltraRAG accepts mixed step forms:\n\n### 5.1 Plain step\n\n```yaml\n- retriever.retriever_search\n```\n\n### 5.2 Step with input/output remapping\n\n```yaml\n- generation.generate:\n    input:\n      prompt_ls: custom_prompt_ls\n    output:\n      ans_ls: final_answer_ls\n```\n\n### 5.3 Loop block\n\n```yaml\n- loop:\n    times: 3\n    steps:\n    - retriever.retriever_search\n    - generation.generate\n```\n\n### 5.4 Branch block\n\n```yaml\n- branch:\n    router:\n    - router.route_query\n    branches:\n      need_retrieval:\n      - retriever.retriever_search\n      direct_answer:\n      - generation.generate\n```\n\n### 5.5 Prompt vs Tool step semantics\n\n- Steps under the `prompt` server call `client.get_prompt(...)`.\n- Non-prompt steps call `client.call_tool(...)`.\n\n---\n\n## 6) Variable Resolution and Data Flow Rules\n\nIn `UltraData`, each tool input value is interpreted by convention:\n\n- `\"$foo\"` -> load from server-local params (`parameter.yaml`)\n- `\"bar\"`  -> read from global variable pool (`global_vars[\"bar\"]`)\n- `\"memory_xxx\"` -> read/write memory lists\n\nOutput handling:\n\n- Tool returns JSON payload -> keys mapped to declared outputs\n- Output remapping (`output:` in pipeline step) is applied at save time\n- Prompt outputs usually produce `prompt_ls`\n\nBranch handling:\n\n- Branch-aware values use wrapped list records with branch-state keys.\n- Internal sentinel (`UNSET`) is used to distinguish \"not filled yet\" from `None`.\n\nMemory handling:\n\n- The engine tracks `memory_*` histories automatically.\n- Final snapshots are serialized to `output/memory_<...>.json`.\n- If a memory server is detected, turn-level memory auto-save can be triggered.\n\n---\n\n## 7) Core Python Modules (Authoritative Guide)\n\n### `src/ultrarag/client.py`\n\nThis is the orchestration heart of the project.\n\nKey responsibilities:\n\n- CLI entrypoint (`main`)\n- UI launch (`launch_ui`) and case-study launch (`launch_case_study`)\n- Config loading (`Configuration`)\n- Pipeline data graph and state (`UltraData`)\n- Build pipeline configs (`build`)\n- Load runtime context (`load_pipeline_context`)\n- Execute step engine (`execute_pipeline`)\n- Run full pipeline (`run`)\n\nImportant runtime behaviors:\n\n- Supports both local python MCP servers (`path.endswith(\".py\")`) and remote MCP endpoints (`http(s)`).\n- For remote MCP, requires Node.js >= 20 and uses `npx -y mcp-remote <url>`.\n- Keeps loop-termination state in `ContextVar` for coroutine safety.\n- Emits structured stream events in demo/UI flows (`step_start`, `step_end`, `token`, `sources`).\n\n### `src/ultrarag/server.py`\n\nDefines `UltraRAG_MCP_Server`, a compatibility wrapper over FastMCP.\n\nKey responsibilities:\n\n- Enhanced `tool()` and `prompt()` registration with `output` metadata support\n- Metadata capture for automatic config generation\n- `build(parameter_file)` to generate per-server `server.yaml`\n- Compatibility filtering for FastMCP signature differences\n\n### `src/ultrarag/api.py`\n\nProvides ergonomic Python-side wrappers:\n\n- `initialize(servers, server_root, log_level)`\n- `ToolCall.server_name.tool_name(...)`\n- `PipelineCall(pipeline_file, parameter_file, log_level)`\n\n### `src/ultrarag/mcp_logging.py`\n\n- Initializes root logger `UltraRAG`\n- Rich console logging + file logging (`logs/<timestamp>.log`)\n- Log level controlled by `log_level` argument and environment\n\n### `src/ultrarag/mcp_exceptions.py`\n\n- Validates local Node.js availability/version\n- Raises `NodeNotInstalledError` / `NodeVersionTooLowError`\n\n### `src/ultrarag/utils.py`\n\n- Subprocess lifecycle helpers\n- POSIX parent-death signal support\n- Windows job object support for child-process cleanup\n\n---\n\n## 8) MCP Server Authoring Contract\n\nEach server follows this shape:\n\n```text\nservers/<name>/\n├── parameter.yaml\n├── server.yaml            # generated\n└── src/<name>.py\n```\n\n### 8.1 Registration styles\n\nUse either:\n\n1. Decorator style\n2. Class-bound method registration style\n\nBoth are valid in this codebase.\n\n### 8.2 `output=` grammar\n\nCanonical form:\n\n```text\ninput1,input2,$param_a -> output1,output2\n```\n\nRules:\n\n- Left side maps function args to pipeline inputs.\n- Right side defines expected output keys from returned dict/JSON.\n- `-> None` means no output variables.\n- `$param` means value comes from server parameter file.\n\n### 8.3 Return payload expectations\n\n- For tools: return JSON-serializable dict payloads matching declared output keys.\n- For prompts: return prompt messages (typically list-like prompt payloads consumed by `get_prompt`).\n\n### 8.4 Entrypoint requirement\n\nServer modules should end with:\n\n```python\nif __name__ == \"__main__\":\n    app.run(transport=\"stdio\")\n```\n\n---\n\n## 9) Retriever/Generation/Prompt Specific Notes\n\n### Retriever (`servers/retriever`)\n\n- Supports multiple retrieval modes:\n  - Dense retrieval\n  - BM25\n  - Web search\n  - Project-memory retrieval\n- Index backends are pluggable via factory:\n  - `faiss`\n  - `milvus`\n  - `qdrant`\n- Web search backends are pluggable via factory:\n  - `exa`\n  - `tavily`\n  - `zhipuai`\n  - `serply`\n\n### Generation (`servers/generation`)\n\n- Supports generation backends including `openai`, `vllm`, and hf workflows.\n- Provides explicit cleanup tool `vllm_shutdown`.\n- Demo mode can use local streaming generation service.\n\n### Prompt (`servers/prompt`)\n\n- Uses `SandboxedEnvironment` from Jinja2 for safer rendering.\n- Validates template paths to reduce traversal risk.\n- Escapes string inputs before template rendering.\n\n---\n\n## 10) UI Backend Architecture (`ui/backend`)\n\n### `app.py`\n\n- Flask app factory (`create_app`)\n- Serves frontend static assets\n- Exposes chat/pipeline/KB/auth endpoints\n- Reads optional frontend override via `ULTRARAG_FRONTEND_DIR`\n\n### `pipeline_manager.py`\n\nThis module is large and central to UI behavior.\n\nMajor responsibilities:\n\n- Session lifecycle and streaming chat management\n- Background chat task management\n- Pipeline CRUD (list/load/save/rename/delete)\n- Parameter load/save/build wrappers\n- Knowledge base file ingest and pipeline triggering\n- Memory synchronization to per-user KB collections\n- Optional server introspection via AST stub generation if `server.yaml` is missing\n\nNotable behavior:\n\n- Applies defensive patches to suppress noisy closed-event-loop teardown logs.\n- Uses a queue bridge for async-to-sync SSE event streaming.\n- Supports automatic memory-to-KB sync for pipelines that include memory components.\n\n---\n\n## 11) Storage Model and Paths\n\nDefault UI storage root:\n\n- `ui/storage`\n\nCan be overridden by:\n\n- `ULTRARAG_UI_STORAGE_ROOT`\n\nKey subpaths:\n\n- `db/users.sqlite3`\n- `chat_sessions/`\n- `knowledge_base/raw|corpus|chunks|index`\n- `memory/`\n- `knowledge_base/_memory_sync`\n\nRuntime outputs:\n\n- `output/memory_*.json`\n- `logs/*.log`\n\n---\n\n## 12) Environment Variables You Should Know\n\n- `ULTRARAG_UI_STORAGE_ROOT`: override UI storage root\n- `ULTRARAG_FRONTEND_DIR`: override frontend static directory\n- `ULTRARAG_SESSION_TIMEOUT`: foreground chat session timeout\n- `ULTRARAG_BG_SESSION_TIMEOUT`: background session timeout\n- `ULTRARAG_LOG_TS`: custom timestamp seed for log file naming\n- `log_level`: consumed by core logger initialization\n\n---\n\n## 13) Dependency Model\n\nInstall tiers from `pyproject.toml`:\n\n- Core install: no extras\n- `retriever` extra\n- `generation` extra\n- `evaluation` extra\n- `corpus` extra\n- `all` extra (union)\n\nTypical commands:\n\n```bash\nuv sync\nuv sync --extra retriever\nuv sync --extra generation\nuv sync --all-extras\n```\n\nDevelopment dependencies include:\n\n- `ruff`\n- `ipython`\n- `jupyter`\n- `pytest`\n\n---\n\n## 14) CLI Commands (Canonical)\n\n```bash\nultrarag build <pipeline.yaml>\nultrarag run <pipeline.yaml> [--param <parameter.yaml>] [--log_level info|debug|warn|error] [--is_demo]\nultrarag show ui [--host 127.0.0.1] [--port 5050]\nultrarag show case [--config_path <memory.json>] [--host 127.0.0.1] [--port 8080]\n```\n\nMinimal smoke check:\n\n```bash\nultrarag run examples/experiments/sayhello.yaml\n```\n\n---\n\n## 15) Docker Variants\n\n- `Dockerfile`: full image (builds frontend, installs all extras)\n- `Dockerfile.base-cpu`: CPU base image\n- `Dockerfile.base-gpu`: GPU base image\n\nAll variants start UI with:\n\n```bash\nultrarag show ui --port 5050 --host 0.0.0.0\n```\n\n---\n\n## 16) Development Playbooks\n\n### 16.1 Add a new MCP server\n\n1. Create `servers/<name>/parameter.yaml`\n2. Implement `servers/<name>/src/<name>.py`\n3. Register tools/prompts via `app.tool` / `app.prompt` (or class-bound registration)\n4. Ensure `app.run(transport=\"stdio\")` exists\n5. Add server to `servers:` in a pipeline YAML\n6. Run `ultrarag build <pipeline.yaml>`\n\n### 16.2 Add a new tool to an existing server\n\n1. Implement function/method\n2. Register with explicit `output=...` contract\n3. Ensure return payload keys match outputs\n4. Update relevant parameter keys in `parameter.yaml` if using `$...`\n5. Add the step in pipeline YAML and rebuild\n\n### 16.3 Add a retriever index backend\n\n1. Implement backend in `servers/retriever/src/index_backends/`\n2. Follow `BaseIndexBackend` contract\n3. Register in `_INDEX_BACKENDS` map in `index_backends/__init__.py`\n\n### 16.4 Add a web-search backend\n\n1. Implement backend in `servers/retriever/src/websearch_backends/`\n2. Follow `BaseWebSearchBackend` contract\n3. Register in `_WEBSEARCH_BACKENDS` map\n\n### 16.5 Modify UI pipeline behavior\n\nPrimary files:\n\n- `ui/backend/app.py`\n- `ui/backend/pipeline_manager.py`\n\nIf touching build/runtime semantics, cross-check against:\n\n- `src/ultrarag/client.py`\n\n---\n\n## 17) Coding Standards (Repository-Conformant)\n\n- Use type hints for function signatures.\n- Prefer `pathlib.Path` for filesystem paths.\n- Use `yaml.safe_load` / `yaml.safe_dump`.\n- Use project logger (`get_logger` or `app.logger`) instead of `print`.\n- Keep tool outputs deterministic and JSON-serializable.\n- Keep imports grouped: stdlib -> third-party -> local.\n- Keep async boundaries explicit (`async`/`await`).\n\n---\n\n## 18) What Not To Edit Blindly\n\nTreat these as generated or runtime artifacts unless intentionally regenerating:\n\n- `servers/*/server.yaml`\n- `examples/**/parameter/*_parameter.yaml`\n- `examples/**/server/*_server.yaml`\n- `output/*`\n- `logs/*`\n- `ui/storage/*` runtime data\n\nAlso avoid committing secrets:\n\n- `.env`\n- any credential-bearing local config\n\n---\n\n## 19) Common Failure Modes and Fixes\n\n### Error: server file not found\n\n- Check `servers.<name>` path in pipeline YAML.\n- Ensure server entry script exists at `servers/<name>/src/<name>.py` (or update path in config).\n\n### Error: missing variable in pipeline execution\n\n- Verify output key names from upstream tool match downstream input names.\n- Verify remapping under step-level `output:` is correct.\n- Verify `$param` keys exist in that server's parameter config.\n\n### Remote MCP server fails to start\n\n- Ensure Node.js >= 20.\n- Confirm `npx` is available.\n- Confirm remote URL in server path is reachable.\n\n### Build succeeds but UI cannot list tools\n\n- Ensure `server.yaml` exists or can be inferred by AST parsing.\n- Check for unusual dynamic registration patterns that static analysis cannot infer.\n\n### No final answer in chat\n\n- Inspect `output/memory_*.json`.\n- Check whether generation step ran and produced `ans_ls`.\n- Check stream events in UI path (`step_start`, `step_end`, `sources`, `token`, `final`).\n\n---\n\n## 20) Validation Checklist for Agents\n\nBefore finalizing any non-trivial change:\n\n1. Build the affected pipeline:\n   - `ultrarag build <pipeline.yaml>`\n2. Run a relevant smoke case:\n   - `ultrarag run <pipeline.yaml>`\n3. If UI behavior changed:\n   - run `ultrarag show ui` and verify route behavior\n4. If retriever/generation backends changed:\n   - validate parameter schema keys and output names\n5. Keep generated artifacts intentional:\n   - do not accidentally commit transient runtime outputs\n\n---\n\n## 21) Minimal Quickstart for New Agents\n\n```bash\n# 1) Install dependencies\nuv sync --all-extras\n\n# 2) Smoke test\nultrarag run examples/experiments/sayhello.yaml\n\n# 3) Build and run a demo pipeline\nultrarag build examples/demos/LLM.yaml\nultrarag run examples/demos/LLM.yaml\n\n# 4) Launch UI\nultrarag show ui --host 127.0.0.1 --port 5050\n```\n\n---\n\n## 22) Final Notes\n\n- This repository is orchestration-first: correctness depends heavily on I/O naming consistency across tools and pipeline steps.\n- Most regressions come from mismatched variable names, stale generated configs, or incomplete parameter updates.\n- When in doubt, inspect `src/ultrarag/client.py` first: it is the execution truth.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/OpenBMB/UltraRAG/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md\n\nThis document is the primary engineering guide for autonomous coding agents working in the `UltraRAG` repository.\n\nUse this file as the source of truth for architecture, conventions, workflows, and safe change patterns.\nIf `CLAUDE.md` exists, it should only point to this file.\n\n---\n\n## 1) Project Identity\n\n`UltraRAG` is a lightweight RAG framework built around the Model Context Protocol (MCP).\nThe key design choice is strict modularization: retrieval, prompting, generation, routing, memory, and evaluation are implemented as independent MCP servers orchestrated by YAML pipelines.\n\nCurrent core metadata:\n\n- Package: `ultrarag`\n- Version: `0.3.0`\n- Python: `>=3.11, <3.13`\n- CLI entrypoint: `ultrarag = ultrarag.client:main`\n- Package manager: `uv` (`[tool.uv] package = true`)\n\n---\n\n## 2) Repository Map (What Matters Most)\n\n```text\nUltraRAG/\n├── src/ultrarag/                    # Installable core package\n│   ├── client.py                    # CLI + pipeline engine + run/build orchestration\n│   ├── server.py                    # UltraRAG_MCP_Server (FastMCP extension)\n│   ├── api.py                       # Python API wrappers (ToolCall, PipelineCall)\n│   ├── cli.py                       # Rich banner and CLI visuals\n│   ├── mcp_logging.py               # Central logging setup\n│   ├── mcp_exceptions.py            # Node.js checks for remote MCP\n│   └── utils.py                     # Subprocess lifecycle helpers\n│\n├── servers/                         # MCP microservices (each server is independent)\n│   ├── retriever/\n│   ├── generation/\n│   ├── prompt/\n│   ├── reranker/\n│   ├── benchmark/\n│   ├── evaluation/\n│   ├── corpus/\n│   ├── memory/\n│   ├── router/\n│   ├── custom/\n│   ├── pageindex/\n│   └── sayhello/\n│\n├── examples/\n│   ├── demos/                       # UI-ready demo pipelines\n│   └── experiments/                 # Experiment/research pipelines\n│\n├── ui/\n│   ├── backend/                     # Flask backend + pipeline manager\n│   └── frontend/                    # Vite + React + TypeScript\n│\n├── docs/                            # Docs and assets\n├── script/                          # Utility scripts (deploy, case study, etc.)\n├── pyproject.toml                   # Dependencies + package metadata\n├── uv.lock                          # Locked dependency graph\n├── Dockerfile*                      # Container variants\n└── .gitignore\n```\n\nImportant generated/derived files:\n\n- `servers/*/server.yaml` (generated by each server `build` tool)\n- `examples/**/parameter/*_parameter.yaml` (pipeline-merged parameters)\n- `examples/**/server/*_server.yaml` (pipeline-merged server config)\n- `output/memory_*.json` (per-run memory snapshots)\n\n---\n\n## 3) Mental Model of the System\n\nThink of UltraRAG as a three-layer system:\n\n1. **Interface layer**: CLI (`ultrarag ...`), UI (`ultrarag show ui`), and Python API (`ToolCall`, `PipelineCall`)\n2. **Orchestration layer**: `src/ultrarag/client.py` (`build`, `load_pipeline_context`, `execute_pipeline`)\n3. **Execution layer**: MCP servers in `servers/*`, each exposing tools/prompts over stdio (or remote MCP proxy)\n\nThe runtime contract is:\n\n- A pipeline YAML declares **which servers** to use and **which steps** to execute.\n- The client resolves I/O dependencies between steps.\n- Each step calls exactly one MCP tool or prompt.\n- Outputs are saved to a shared variable pool and can feed downstream steps.\n\n---\n\n## 4) Two-Phase Execution Lifecycle\n\n### Phase A: Build\n\nCommand:\n\n```bash\nultrarag build <pipeline.yaml>\n```\n\nWhat happens:\n\n- Reads `servers:` from the pipeline YAML.\n- For each referenced server, calls the server's `build` tool.\n- Produces:\n  - `<pipeline_dir>/parameter/<pipeline_name>_parameter.yaml`\n  - `<pipeline_dir>/server/<pipeline_name>_server.yaml`\n\nWhy this matters:\n\n- `build` materializes exact tool/prompt I/O metadata before runtime.\n- UI and runner rely on these generated artifacts for deterministic execution.\n\n### Phase B: Run\n\nCommand:\n\n```bash\nultrarag run <pipeline.yaml> [--param path] [--is_demo]\n```\n\nWhat happens:\n\n- Loads generated server config + parameter config.\n- Creates `fastmcp.Client` transport config for each server.\n- Executes pipeline steps in order, including `loop` and `branch`.\n- Saves intermediate memory snapshots and writes `output/memory_*.json`.\n- Invokes cleanup tools (e.g., tools ending with `vllm_shutdown`) if present.\n\n---\n\n## 5) Pipeline DSL Reference\n\nUltraRAG accepts mixed step forms:\n\n### 5.1 Plain step\n\n```yaml\n- retriever.retriever_search\n```\n\n### 5.2 Step with input/output remapping\n\n```yaml\n- generation.generate:\n    input:\n      prompt_ls: custom_prompt_ls\n    output:\n      ans_ls: final_answer_ls\n```\n\n### 5.3 Loop block\n\n```yaml\n- loop:\n    times: 3\n    steps:\n    - retriever.retriever_search\n    - generation.generate\n```\n\n### 5.4 Branch block\n\n```yaml\n- branch:\n    router:\n    - router.route_query\n    branches:\n      need_retrieval:\n      - retriever.retriever_search\n      direct_answer:\n      - generation.generate\n```\n\n### 5.5 Prompt vs Tool step semantics\n\n- Steps under the `prompt` server call `client.get_prompt(...)`.\n- Non-prompt steps call `client.call_tool(...)`.\n\n---\n\n## 6) Variable Resolution and Data Flow Rules\n\nIn `UltraData`, each tool input value is interpreted by convention:\n\n- `\"$foo\"` -> load from server-local params (`parameter.yaml`)\n- `\"bar\"`  -> read from global variable pool (`global_vars[\"bar\"]`)\n- `\"memory_xxx\"` -> read/write memory lists\n\nOutput handling:\n\n- Tool returns JSON payload -> keys mapped to declared outputs\n- Output remapping (`output:` in pipeline step) is applied at save time\n- Prompt outputs usually produce `prompt_ls`\n\nBranch handling:\n\n- Branch-aware values use wrapped list records with branch-state keys.\n- Internal sentinel (`UNSET`) is used to distinguish \"not filled yet\" from `None`.\n\nMemory handling:\n\n- The engine tracks `memory_*` histories automatically.\n- Final snapshots are serialized to `output/memory_<...>.json`.\n- If a memory server is detected, turn-level memory auto-save can be triggered.\n\n---\n\n## 7) Core Python Modules (Authoritative Guide)\n\n### `src/ultrarag/client.py`\n\nThis is the orchestration heart of the project.\n\nKey responsibilities:\n\n- CLI entrypoint (`main`)\n- UI launch (`launch_ui`) and case-study launch (`launch_case_study`)\n- Config loading (`Configuration`)\n- Pipeline data graph and state (`UltraData`)\n- Build pipeline configs (`build`)\n- Load runtime context (`load_pipeline_context`)\n- Execute step engine (`execute_pipeline`)\n- Run full pipeline (`run`)\n\nImportant runtime behaviors:\n\n- Supports both local python MCP servers (`path.endswith(\".py\")`) and remote MCP endpoints (`http(s)`).\n- For remote MCP, requires Node.js >= 20 and uses `npx -y mcp-remote <url>`.\n- Keeps loop-termination state in `ContextVar` for coroutine safety.\n- Emits structured stream events in demo/UI flows (`step_start`, `step_end`, `token`, `sources`).\n\n### `src/ultrarag/server.py`\n\nDefines `UltraRAG_MCP_Server`, a compatibility wrapper over FastMCP.\n\nKey responsibilities:\n\n- Enhanced `tool()` and `prompt()` registration with `output` metadata support\n- Metadata capture for automatic config generation\n- `build(parameter_file)` to generate per-server `server.yaml`\n- Compatibility filtering for FastMCP signature differences\n\n### `src/ultrarag/api.py`\n\nProvides ergonomic Python-side wrappers:\n\n- `initialize(servers, server_root, log_level)`\n- `ToolCall.server_name.tool_name(...)`\n- `PipelineCall(pipeline_file, parameter_file, log_level)`\n\n### `src/ultrarag/mcp_logging.py`\n\n- Initializes root logger `UltraRAG`\n- Rich console logging + file logging (`logs/<timestamp>.log`)\n- Log level controlled by `log_level` argument and environment\n\n### `src/ultrarag/mcp_exceptions.py`\n\n- Validates local Node.js availability/version\n- Raises `NodeNotInstalledError` / `NodeVersionTooLowError`\n\n### `src/ultrarag/utils.py`\n\n- Subprocess lifecycle helpers\n- POSIX parent-death signal support\n- Windows job object support for child-process cleanup\n\n---\n\n## 8) MCP Server Authoring Contract\n\nEach server follows this shape:\n\n```text\nservers/<name>/\n├── parameter.yaml\n├── server.yaml            # generated\n└── src/<name>.py\n```\n\n### 8.1 Registration styles\n\nUse either:\n\n1. Decorator style\n2. Class-bound method registration style\n\nBoth are valid in this codebase.\n\n### 8.2 `output=` grammar\n\nCanonical form:\n\n```text\ninput1,input2,$param_a -> output1,output2\n```\n\nRules:\n\n- Left side maps function args to pipeline inputs.\n- Right side defines expected output keys from returned dict/JSON.\n- `-> None` means no output variables.\n- `$param` means value comes from server parameter file.\n\n### 8.3 Return payload expectations\n\n- For tools: return JSON-serializable dict payloads matching declared output keys.\n- For prompts: return prompt messages (typically list-like prompt payloads consumed by `get_prompt`).\n\n### 8.4 Entrypoint requirement\n\nServer modules should end with:\n\n```python\nif __name__ == \"__main__\":\n    app.run(transport=\"stdio\")\n```\n\n---\n\n## 9) Retriever/Generation/Prompt Specific Notes\n\n### Retriever (`servers/retriever`)\n\n- Supports multiple retrieval modes:\n  - Dense retrieval\n  - BM25\n  - Web search\n  - Project-memory retrieval\n- Index backends are pluggable via factory:\n  - `faiss`\n  - `milvus`\n  - `qdrant`\n- Web search backends are pluggable via factory:\n  - `exa`\n  - `tavily`\n  - `zhipuai`\n  - `serply`\n\n### Generation (`servers/generation`)\n\n- Supports generation backends including `openai`, `vllm`, and hf workflows.\n- Provides explicit cleanup tool `vllm_shutdown`.\n- Demo mode can use local streaming generation service.\n\n### Prompt (`servers/prompt`)\n\n- Uses `SandboxedEnvironment` from Jinja2 for safer rendering.\n- Validates template paths to reduce traversal risk.\n- Escapes string inputs before template rendering.\n\n---\n\n## 10) UI Backend Architecture (`ui/backend`)\n\n### `app.py`\n\n- Flask app factory (`create_app`)\n- Serves frontend static assets\n- Exposes chat/pipeline/KB/auth endpoints\n- Reads optional frontend override via `ULTRARAG_FRONTEND_DIR`\n\n### `pipeline_manager.py`\n\nThis module is large and central to UI behavior.\n\nMajor responsibilities:\n\n- Session lifecycle and streaming chat management\n- Background chat task management\n- Pipeline CRUD (list/load/save/rename/delete)\n- Parameter load/save/build wrappers\n- Knowledge base file ingest and pipeline triggering\n- Memory synchronization to per-user KB collections\n- Optional server introspection via AST stub generation if `server.yaml` is missing\n\nNotable behavior:\n\n- Applies defensive patches to suppress noisy closed-event-loop teardown logs.\n- Uses a queue bridge for async-to-sync SSE event streaming.\n- Supports automatic memory-to-KB sync for pipelines that include memory components.\n\n---\n\n## 11) Storage Model and Paths\n\nDefault UI storage root:\n\n- `ui/storage`\n\nCan be overridden by:\n\n- `ULTRARAG_UI_STORAGE_ROOT`\n\nKey subpaths:\n\n- `db/users.sqlite3`\n- `chat_sessions/`\n- `knowledge_base/raw|corpus|chunks|index`\n- `memory/`\n- `knowledge_base/_memory_sync`\n\nRuntime outputs:\n\n- `output/memory_*.json`\n- `logs/*.log`\n\n---\n\n## 12) Environment Variables You Should Know\n\n- `ULTRARAG_UI_STORAGE_ROOT`: override UI storage root\n- `ULTRARAG_FRONTEND_DIR`: override frontend static directory\n- `ULTRARAG_SESSION_TIMEOUT`: foreground chat session timeout\n- `ULTRARAG_BG_SESSION_TIMEOUT`: background session timeout\n- `ULTRARAG_LOG_TS`: custom timestamp seed for log file naming\n- `log_level`: consumed by core logger initialization\n\n---\n\n## 13) Dependency Model\n\nInstall tiers from `pyproject.toml`:\n\n- Core install: no extras\n- `retriever` extra\n- `generation` extra\n- `evaluation` extra\n- `corpus` extra\n- `all` extra (union)\n\nTypical commands:\n\n```bash\nuv sync\nuv sync --extra retriever\nuv sync --extra generation\nuv sync --all-extras\n```\n\nDevelopment dependencies include:\n\n- `ruff`\n- `ipython`\n- `jupyter`\n- `pytest`\n\n---\n\n## 14) CLI Commands (Canonical)\n\n```bash\nultrarag build <pipeline.yaml>\nultrarag run <pipeline.yaml> [--param <parameter.yaml>] [--log_level info|debug|warn|error] [--is_demo]\nultrarag show ui [--host 127.0.0.1] [--port 5050]\nultrarag show case [--config_path <memory.json>] [--host 127.0.0.1] [--port 8080]\n```\n\nMinimal smoke check:\n\n```bash\nultrarag run examples/experiments/sayhello.yaml\n```\n\n---\n\n## 15) Docker Variants\n\n- `Dockerfile`: full image (builds frontend, installs all extras)\n- `Dockerfile.base-cpu`: CPU base image\n- `Dockerfile.base-gpu`: GPU base image\n\nAll variants start UI with:\n\n```bash\nultrarag show ui --port 5050 --host 0.0.0.0\n```\n\n---\n\n## 16) Development Playbooks\n\n### 16.1 Add a new MCP server\n\n1. Create `servers/<name>/parameter.yaml`\n2. Implement `servers/<name>/src/<name>.py`\n3. Register tools/prompts via `app.tool` / `app.prompt` (or class-bound registration)\n4. Ensure `app.run(transport=\"stdio\")` exists\n5. Add server to `servers:` in a pipeline YAML\n6. Run `ultrarag build <pipeline.yaml>`\n\n### 16.2 Add a new tool to an existing server\n\n1. Implement function/method\n2. Register with explicit `output=...` contract\n3. Ensure return payload keys match outputs\n4. Update relevant parameter keys in `parameter.yaml` if using `$...`\n5. Add the step in pipeline YAML and rebuild\n\n### 16.3 Add a retriever index backend\n\n1. Implement backend in `servers/retriever/src/index_backends/`\n2. Follow `BaseIndexBackend` contract\n3. Register in `_INDEX_BACKENDS` map in `index_backends/__init__.py`\n\n### 16.4 Add a web-search backend\n\n1. Implement backend in `servers/retriever/src/websearch_backends/`\n2. Follow `BaseWebSearchBackend` contract\n3. Register in `_WEBSEARCH_BACKENDS` map\n\n### 16.5 Modify UI pipeline behavior\n\nPrimary files:\n\n- `ui/backend/app.py`\n- `ui/backend/pipeline_manager.py`\n\nIf touching build/runtime semantics, cross-check against:\n\n- `src/ultrarag/client.py`\n\n---\n\n## 17) Coding Standards (Repository-Conformant)\n\n- Use type hints for function signatures.\n- Prefer `pathlib.Path` for filesystem paths.\n- Use `yaml.safe_load` / `yaml.safe_dump`.\n- Use project logger (`get_logger` or `app.logger`) instead of `print`.\n- Keep tool outputs deterministic and JSON-serializable.\n- Keep imports grouped: stdlib -> third-party -> local.\n- Keep async boundaries explicit (`async`/`await`).\n\n---\n\n## 18) What Not To Edit Blindly\n\nTreat these as generated or runtime artifacts unless intentionally regenerating:\n\n- `servers/*/server.yaml`\n- `examples/**/parameter/*_parameter.yaml`\n- `examples/**/server/*_server.yaml`\n- `output/*`\n- `logs/*`\n- `ui/storage/*` runtime data\n\nAlso avoid committing secrets:\n\n- `.env`\n- any credential-bearing local config\n\n---\n\n## 19) Common Failure Modes and Fixes\n\n### Error: server file not found\n\n- Check `servers.<name>` path in pipeline YAML.\n- Ensure server entry script exists at `servers/<name>/src/<name>.py` (or update path in config).\n\n### Error: missing variable in pipeline execution\n\n- Verify output key names from upstream tool match downstream input names.\n- Verify remapping under step-level `output:` is correct.\n- Verify `$param` keys exist in that server's parameter config.\n\n### Remote MCP server fails to start\n\n- Ensure Node.js >= 20.\n- Confirm `npx` is available.\n- Confirm remote URL in server path is reachable.\n\n### Build succeeds but UI cannot list tools\n\n- Ensure `server.yaml` exists or can be inferred by AST parsing.\n- Check for unusual dynamic registration patterns that static analysis cannot infer.\n\n### No final answer in chat\n\n- Inspect `output/memory_*.json`.\n- Check whether generation step ran and produced `ans_ls`.\n- Check stream events in UI path (`step_start`, `step_end`, `sources`, `token`, `final`).\n\n---\n\n## 20) Validation Checklist for Agents\n\nBefore finalizing any non-trivial change:\n\n1. Build the affected pipeline:\n   - `ultrarag build <pipeline.yaml>`\n2. Run a relevant smoke case:\n   - `ultrarag run <pipeline.yaml>`\n3. If UI behavior changed:\n   - run `ultrarag show ui` and verify route behavior\n4. If retriever/generation backends changed:\n   - validate parameter schema keys and output names\n5. Keep generated artifacts intentional:\n   - do not accidentally commit transient runtime outputs\n\n---\n\n## 21) Minimal Quickstart for New Agents\n\n```bash\n# 1) Install dependencies\nuv sync --all-extras\n\n# 2) Smoke test\nultrarag run examples/experiments/sayhello.yaml\n\n# 3) Build and run a demo pipeline\nultrarag build examples/demos/LLM.yaml\nultrarag run examples/demos/LLM.yaml\n\n# 4) Launch UI\nultrarag show ui --host 127.0.0.1 --port 5050\n```\n\n---\n\n## 22) Final Notes\n\n- This repository is orchestration-first: correctness depends heavily on I/O naming consistency across tools and pipeline steps.\n- Most regressions come from mismatched variable names, stale generated configs, or incomplete parameter updates.\n- When in doubt, inspect `src/ultrarag/client.py` first: it is the execution truth.\n","isInternal":false,"tokens":4141,"sizeBytes":17082},{"name":"llms.txt","path":"docs/llms.txt","rawUrl":"https://raw.githubusercontent.com/OpenBMB/UltraRAG/HEAD/docs/llms.txt","title":"docs - LLM Index & Context Digest","category":"root-instruction","format":"text","content":"This file is a merged representation of the entire codebase, combining all repository files into a single document.\nGenerated by Repopack on: 2026-01-18T09:46:51.430Z\n\n================================================================\nFile Summary\n================================================================\n\nPurpose:\n--------\nThis file contains a packed representation of the entire repository's contents.\nIt is designed to be easily consumable by AI systems for analysis, code review,\nor other automated processes.\n\nFile Format:\n------------\nThe content is organized as follows:\n1. This summary section\n2. Repository information\n3. Repository structure\n4. Multiple file entries, each consisting of:\n  a. A separator line (================)\n  b. The file path (File: path/to/file)\n  c. Another separator line\n  d. The full contents of the file\n  e. A blank line\n\nUsage Guidelines:\n-----------------\n- This file should be treated as read-only. Any changes should be made to the\n  original repository files, not this packed version.\n- When processing this file, use the file path to distinguish\n  between different files in the repository.\n- Be aware that this file may contain sensitive information. Handle it with\n  the same level of security as you would the original repository.\n\nNotes:\n------\n- Some files may have been excluded based on .gitignore rules and Repopack's\n  configuration.\n- Binary files are not included in this packed representation. Please refer to\n  the Repository Structure section for a complete list of file paths, including\n  binary files.\n\nAdditional Info:\n----------------\n\nFor more information about Repopack, visit: https://github.com/yamadashy/repopack\n\n================================================================\nRepository Structure\n================================================================\npages/\n  en/\n    api/\n      benchmark.mdx\n      cli.mdx\n      corpus.mdx\n      custom.mdx\n      evaluation.mdx\n      generation.mdx\n      prompt.mdx\n      reranker.mdx\n      retriever.mdx\n      router.mdx\n    demo/\n      deepresearch.mdx\n      lightresearch.mdx\n      llm.mdx\n      rag.mdx\n    develop_guide/\n      case_study.mdx\n      code_integration.mdx\n      dataset.mdx\n      debug.mdx\n      parallel.mdx\n    getting_started/\n      installation.mdx\n      introduction.mdx\n      quick_start.mdx\n      update.mdx\n    pipeline/\n      light_deepresearch.mdx\n      rag.mdx\n      search_o1.mdx\n      visrag.mdx\n    rag_client/\n      branch.mdx\n      data_and_params.mdx\n      loop.mdx\n      multi_agents.mdx\n      pipeline.mdx\n      serial.mdx\n    rag_servers/\n      benchmark.mdx\n      corpus.mdx\n      custom.mdx\n      evaluation.mdx\n      generation.mdx\n      overview.mdx\n      prompt.mdx\n      reranker.mdx\n      retriever.mdx\n      router.mdx\n    ui/\n      prepare.mdx\n      start.mdx\n\n================================================================\nRepository Files\n================================================================\n\n================\nFile: pages/en/api/benchmark.mdx\n================\n---\ntitle: \"Benchmark\"\nicon: \"chart-line\"\n---\n\n## `get_data`\n\n**Signature**\n```python\n@app.tool(output=\"benchmark->q_ls,gt_ls\")\ndef get_data(benchmark: Dict[str, Any]) -> Dict[str, List[Any]]\n```\n\n**Function**\n- Multi-format Loading: Supports loading evaluation datasets in `.jsonl`, `.json`, or `.parquet` formats from local storage.\n- Dynamic Field Mapping: Uses `key_map` to map different column names in raw data (such as `question`, `answer`) to standardized output keys (usually `q_ls` and `gt_ls`).\n- Data Preprocessing: Built-in support for random shuffling (`shuffle`) and quantity truncation (`limit`).\n- Used in Demos to receive user input, treating it as a piece of data (`q_ls`).\n\n**Output Format (JSON)**\n```json\n{\n  \"q_ls\": [\"Question 1\", \"Question 2\"],\n  \"gt_ls\": [[\"Answer A1\", \"Answer A2\"], [\"Answer B\"]]\n}\n```\n\n---\n\n## Configuration\n\n```yaml servers/benchmark/parameter.yaml icon=\"/images/yaml.svg\"\nbenchmark:\n  name: nq\n  path: data/sample_nq_10.jsonl\n  key_map:\n    q_ls: question\n    gt_ls: golden_answers\n  shuffle: false\n  seed: 42\n  limit: -1\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `name` | str | Evaluation set name, used only for logging and identification (Example: `nq`) |\n| `path` | str | Data file path, supports `.jsonl`, `.json`, `.parquet` |\n| `key_map` | dict | Field mapping table, mapping raw fields to tool output keys |\n|  | `q_ls` | str| Raw field name mapped to Question List (e.g., question column in file) |\n|  | `gt_ls` | str| Raw field name mapped to Golden Answer List (e.g., golden_answers column in file) |\n| `shuffle` | bool | Whether to shuffle sample order (default `false`) |\n| `seed` | int | Random seed (effective when `shuffle=true`) |\n| `limit` | int | Upper limit of sampled data items. Default is -1 (load all), positive integer means truncation of first N items |\n\n================\nFile: pages/en/api/cli.mdx\n================\n---\ntitle: \"CLI\"\nicon: \"command\"\n---\n\n## help — Command Overview\n\n```bash\nultrarag --help\n```\n\n```text\nusage: ultrarag [-h] {build,run,show} ...\n\nUltraRAG CLI\n\npositional arguments:\n  {build,run,show}\n    build           Build the configuration\n    run             Run the pipeline with the given configuration\n    show            Show ui interfaces\n\noptions:\n  -h, --help        show this help message and exit\n```\n\n---\n\n## build — Build Configuration\n\n**Purpose**: Automatically generate a unified parameter configuration file based on `pipeline.yaml`.\n\n```bash\nultrarag build <CONFIG> [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `<CONFIG>` | Pipeline configuration file path, e.g., `examples/pipeline.yaml` |\n| `--log_level` | Log level, default `INFO` |\n\n---\n\n## run — Execute Pipeline\n\n**Purpose**: Start Pipeline execution.\n\n```bash\nultrarag run <CONFIG> [--param <PARAM_FILE>] [--is_demo] [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `<CONFIG>` | Path to `pipeline.yaml` to be executed |\n| `--param <PARAM_FILE>` | Custom parameter file path, overrides default files under `parameter/` |\n| `--is_demo` | Enable demo mode |\n| `--log_level` | Log level, default `INFO` |\n\n---\n\n## show ui — Start Web Control Interface\n\n**Purpose**: Start UI interface.\n\n```bash\nultrarag show ui [--host 127.0.0.1] [--port 5050] [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `--host` | Web service binding IP (default `127.0.0.1`) |\n| `--port` | Web service port (default `5050`) |\n| `--log_level` | Log level, default `INFO` |\n\nAccess `http://<host>:<port>` after startup to use the UI.\n\n================\nFile: pages/en/api/corpus.mdx\n================\n---\ntitle: \"Corpus\"\nicon: \"file\"\n---\n\n\n## `build_text_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,text_corpus_save_path->None\")\nasync def build_text_corpus(parse_file_path: str, text_corpus_save_path: str) -> None\n```\n\n**Function**\n- Supports .txt / .md;\n- Supports .docx (reads paragraphs and tables);\n- Supports .pdf / .xps / .oxps / .epub / .mobi / .fb2 (pure text extraction via pymupdf).\n- Recursively processes in directory mode.\n\n**Output Format (JSONL)**\n```json\n{\"id\": \"<stem>\", \"title\": \"<stem>\", \"contents\": \"<full text>\"}\n```\n---\n\n## `build_image_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,image_corpus_save_path->None\")\nasync def build_image_corpus(parse_file_path: str, image_corpus_save_path: str) -> None\n```\n\n**Function**\n- **Only supports PDF**: Renders each page as JPG (RGB) at 144DPI and validates file validity.\n- Recursively processes in directory mode.\n\n**Output Index (JSONL)**\n```json\n{\"id\": 0, \"image_id\": \"paper/page_0.jpg\", \"image_path\": \"image/paper/page_0.jpg\"}\n```\n---\n\n## `mineru_parse`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,mineru_dir,mineru_extra_params->None\")\nasync def mineru_parse(\n    parse_file_path: str, \n    mineru_dir: str, \n    mineru_extra_params: Optional[Dict[str, Any]] = None\n) -> None\n```\n\n**Function**\n- Calls CLI `mineru` to structurally parse PDF/directory and outputs to `mineru_dir`.\n\n---\n\n## `build_mineru_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"mineru_dir,parse_file_path,text_corpus_save_path,image_corpus_save_path->None\")\nasync def build_mineru_corpus(\n    mineru_dir: str, \n    parse_file_path: str, \n    text_corpus_save_path: str, \n    image_corpus_save_path: str\n) -> None\n```\n\n**Function**\n- Aggregates MinerU parsing artifacts into **Text Corpus JSONL** and **Image Index JSONL**.\n\n\n**Output Format (JSONL)**\n- Text:\n```json\n{\"id\": \"<stem>\", \"title\": \"<stem>\", \"contents\": \"<markdown full text>\"}\n```\n- Image:\n```json\n{\"id\": 0, \"image_id\": \"paper/page_0.jpg\", \"image_path\": \"images/paper/page_0.jpg\"}\n```\n---\n\n## `chunk_documents`\n\n**Signature**\n```python\n@app.tool(output=\"raw_chunk_path,chunk_backend_configs,chunk_backend,tokenizer_or_token_counter,chunk_size,chunk_path,use_title->None\")\nasync def chunk_documents(\n    raw_chunk_path: str,\n    chunk_backend_configs: Dict[str, Any],\n    chunk_backend: str = \"token\",\n    tokenizer_or_token_counter: str = \"character\",\n    chunk_size: int = 256,\n    chunk_path: Optional[str] = None,\n    use_title: bool = True,\n) -> None\n```\n\n**Function**\n- Chunks input text corpus (JSONL, containing `id/title/contents`) into paragraphs using selected backend:\n- Chunk Backend: Supports `token` / `sentence` / `recursive`.\n- Tokenizer: `tokenizer_or_token_counter` can be `word`, `character`, or `tiktoken` encoding name (e.g., `gpt2`).\n- Chunk Size: Controls block size via `chunk_size` (overlap defaults to size/4).\n- Optionally appends document title to the beginning of each block (`use_title`).\n\n**Output Format (JSONL)**\n```json\n{\"id\": 0, \"doc_id\": \"paper\", \"title\": \"paper\", \"contents\": \"Chunked text\"}\n```\n\n---\n\n## Configuration\n\n```yaml servers/corpus/parameter.yaml icon=\"/images/yaml.svg\"\n# servers/corpus/parameter.yaml\nparse_file_path: data/UltraRAG.pdf\ntext_corpus_save_path: corpora/text.jsonl\nimage_corpus_save_path: corpora/image.jsonl\n\n# mineru\nmineru_dir: corpora/\nmineru_extra_params:\n  source: modelscope\n\n# chunking parameters\nraw_chunk_path: corpora/text.jsonl\nchunk_path: corpora/chunks.jsonl\nuse_title: false\nchunk_backend: sentence # choices=[\"token\", \"sentence\", \"recursive\"]\ntokenizer_or_token_counter: character\nchunk_size: 512\nchunk_backend_configs:\n  token:\n    chunk_overlap: 50\n  sentence:\n    chunk_overlap: 50\n    min_sentences_per_chunk: 1\n    delim: \"['.', '!', '?', '；', '。', '！', '？', '\\\\n']\"\n  recursive:\n    min_characters_per_chunk: 12\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `parse_file_path` | str | Input file or directory path |\n| `text_corpus_save_path` | str | Text corpus output path (JSONL) |\n| `image_corpus_save_path` | str | Image corpus index output path (JSONL) |\n| `mineru_dir` | str | MinerU output root directory |\n| `mineru_extra_params` | dict | MinerU extra parameters, such as `source`, `layout`, etc. |\n| `raw_chunk_path` | str | Chunking input file path (JSONL format) |\n| `chunk_path` | str | Chunking output path |\n| `use_title` | bool | Whether to append document title to the beginning of each chunk |\n| `chunk_backend` | str | Select chunking method: `token`, `sentence`, `recursive` |\n| `tokenizer_or_token_counter` | str | Tokenizer or counting method. Options: `word`, `character` or `tiktoken` model name (e.g., `gpt2`) |\n| `chunk_size` | int | Target size for each chunk |\n| `chunk_backend_configs` | dict | Configuration items for each chunking method (see below) |\n\n`chunk_backend_configs` Detailed Parameters:\n\n| Backend Type | Parameter | Description |\n|---|---|---|\n| **token** | `chunk_overlap` | Overlapping tokens between chunks |\n| **sentence** | `chunk_overlap` | Overlapping count between chunks |\n|  | `min_sentences_per_chunk` | Minimum number of sentences per chunk |\n|  | `delim` | Sentence delimiter list (Python list in string format) |\n| **recursive** | `min_characters_per_chunk` | Minimum character unit for recursive splitting |\n\n================\nFile: pages/en/api/custom.mdx\n================\n---\ntitle: \"Custom\"\nicon: \"puzzle-piece\"\n---\n\n## Search-R1 Tools\n\n### `search_r1_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef search_r1_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts query content from model response.\n- **Logic**: Uses regex `r\"<search>([^<]*)\"` to extract the content inside the last `<search>` tag. If not found, returns \"There is no query.\"; if the query does not end with `?`, it is automatically completed.\n\n### `r1_searcher_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef r1_searcher_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts query from R1-Searcher response.\n- **Logic**: Uses regex `r\"<|begin_of_query|>([^<]*)\"` to extract the last tag content.\n\n---\n\n## IRCoT & IterRetGen Tools\n\n### `iterretgen_nextquery`\n```python\n@app.tool(output=\"q_ls,ret_psg->nextq_ls\")\ndef iterretgen_nextquery(q_ls: List[str], ans_ls: List[str | Any]) -> Dict[str, List[str]]\n```\n- **Function**: Iterative retrieval generation.\n- **Logic**: `next_query = f\"{q} {ans}\"`. Concatenates original question and generated answer as the Query for the next retrieval.\n\n### `ircot_get_first_sent`\n```python\n@app.tool(output=\"ans_ls->q_ls\")\ndef ircot_get_first_sent(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts the first sentence of the answer (up to period or question/exclamation mark).\n\n### `ircot_extract_ans`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef ircot_extract_ans(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts the final answer.\n- **Logic**: Matches content after `so the answer is [...]`.\n\n---\n\n## Search-o1 Tools\n\n### `search_o1_init_list`\n```python\n@app.tool(output=\"q_ls->total_subq_list,total_reason_list,total_final_info_list\")\ndef search_o1_init_list(q_ls: List[str]) -> Dict[str, List[Any]]\n```\n- **Function**: Initializes accumulation lists required by Search-o1 (sub-questions, reasoning, final info), initially filled with `<PAD>`.\n\n### `search_o1_combine_list`\n```python\n@app.tool(output=\"total_subq_list, extract_query_list, total_reason_list, extract_reason_list->total_subq_list, total_reason_list\")\ndef search_o1_combine_list(...)\n```\n- **Function**: Appends the extracted Query and Reasoning of the current step to the total lists.\n\n### `search_o1_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef search_o1_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts content between `<|begin_search_query|>...<|end_search_query|>`.\n\n### `search_o1_reasoning_extract`\n```python\n@app.tool(output=\"ans_ls->extract_reason_list\")\ndef search_o1_reasoning_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts all text before `<|begin_search_query|>` as the reasoning process.\n\n### `search_o1_extract_final_information`\n```python\n@app.tool(output=\"ans_ls->extract_final_infor_list\")\ndef search_o1_extract_final_information(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts content after `**Final Information**` marker.\n\n---\n\n## Utility Tools\n\n### `output_extract_from_boxed`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef output_extract_from_boxed(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts answer from LaTeX `\\boxed{...}`. Supports nested bracket handling and format cleaning.\n\n### `merge_passages`\n```python\n@app.tool(output=\"temp_psg,ret_psg->ret_psg\")\ndef merge_passages(temp_psg: List[str | Any], ret_psg: List[str | Any]) -> Dict[str, List[str | Any]]\n```\n- **Function**: Appends `temp_psg` list to `ret_psg` list.\n\n### `evisrag_output_extract_from_special`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef evisrag_output_extract_from_special(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts answer from `<answer>...</answer>` tags.\n\n### `assign_citation_ids` / `assign_citation_ids_stateful`\n- `assign_citation_ids`: Assigns citation IDs in the form of `[1]`, `[2]` to retrieved passages.\n- `assign_citation_ids_stateful`: Uses `CitationRegistry` class to maintain global citation IDs (cross-step deduplication).\n- `init_citation_registry`: Resets global citation registry.\n\n---\n\n## SurveyCPM Tools\n\n### `surveycpm_state_init`\n```python\n@app.tool(output=\"instruction_ls->state_ls,cursor_ls,survey_ls,step_ls,extend_time_ls,extend_result_ls,retrieved_info_ls,parsed_ls\")\ndef surveycpm_state_init(instruction_ls: List[str]) -> Dict[str, List]\n```\n- **Function**: Initializes SurveyCPM state machine.\n- **Initial State**: `state=\"search\"`, `cursor=\"outline\"`, `step=0`.\n\n### `surveycpm_parse_search_response`\n```python\n@app.tool(output=\"response_ls,surveycpm_hard_mode->keywords_ls,parsed_ls\")\ndef surveycpm_parse_search_response(response_ls: List[str], surveycpm_hard_mode: bool = True) -> Dict[str, List]\n```\n- **Function**: Parses search instructions (JSON or XML format) generated by the model, extracts keyword list.\n\n### `surveycpm_process_passages`\n```python\n@app.tool(output=\"ret_psg_ls->retrieved_info_ls\")\ndef surveycpm_process_passages(ret_psg_ls: List[List[List[str]]]) -> Dict[str, List[str]]\n```\n- **Function**: Processes retrieved passages, deduplicates, limits quantity (Top-K), and concatenates into string.\n\n### `surveycpm_after_init_plan` / `after_write` / `after_extend`\n- **Function**: Parses Agent response for different stages (initialize outline, write content, extend plan).\n- **Logic**:\n  - Calls `surveycpm_parse_response` to validate format and content.\n  - Updates `survey_ls` (outline structure) and `cursor_ls` (current cursor position) if successful.\n  - Keeps original state for retry if failed.\n\n### `surveycpm_update_state`\n```python\n@app.tool(output=\"state_ls,cursor_ls,extend_time_ls,extend_result_ls,step_ls,parsed_ls,surveycpm_max_step,surveycpm_max_extend_step->state_ls,extend_time_ls,step_ls\")\ndef surveycpm_update_state(...)\n```\n- **Function**: Core state machine logic.\n- **State Transition**:\n  - `search` -> `analyst-init_plan` (cursor=\"outline\")\n  - `search` -> `write` (cursor=section-X)\n  - `write` -> `search` (continue writing) or `analyst-extend_plan` (finished current section)\n  - `analyst-extend_plan` -> `search` (extend success) or `done` (no extension)\n  - Exceeds max steps -> `done`\n\n### `surveycpm_format_output`\n```python\n@app.tool(output=\"survey_ls,instruction_ls->ans_ls\")\ndef surveycpm_format_output(survey_ls: List[str], instruction_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Converts final Survey JSON to Markdown format.\n- **Processing**: Automatically handles heading levels (# ## ###), citation formatting (`\\cite{...}` to `[1]`), and text cleaning.\n\n---\n\n## Configuration\n\n```yaml servers/custom/parameter.yaml icon=\"/images/yaml.svg\"\nsurveycpm_hard_mode: false\nsurveycpm_max_step: 140\nsurveycpm_max_extend_step: 12\n```\n\n| Parameter | Type | Description |\n|---|---|---|\n| `surveycpm_hard_mode` | bool | Whether to enable SurveyCPM's strict parsing mode (validate JSON field integrity) |\n| `surveycpm_max_step` | int | Maximum total execution steps, forced end if exceeded |\n| `surveycpm_max_extend_step` | int | Maximum plan extension times |\n\n================\nFile: pages/en/api/evaluation.mdx\n================\n---\ntitle: \"Evaluation\"\nicon: \"clipboard-check\"\n---\n\n## `evaluate`\n\n**Signature**\n```python\n@app.tool(output=\"pred_ls,gt_ls,metrics,save_path->eval_res\")\ndef evaluate(\n    pred_ls: List[str],\n    gt_ls: List[List[str]],\n    metrics: List[str] | None,\n    save_path: str,\n) -> Dict[str, Any]\n```\n\n**Function**\n- Executes automatic metric evaluation for QA/Generation tasks.\n- Supported Metrics: `acc`, `em`, `coverem`, `stringem`, `f1`, `rouge-1`, `rouge-2`, `rouge-l`.\n- Results are automatically saved as `.json` file and printed as Markdown table.\n\n\n---\n\n## `evaluate_trec`\n\n**Signature**\n```python\n@app.tool(output=\"run_path,qrels_path,ir_metrics,ks,save_path->eval_res\")\ndef evaluate_trec(\n    run_path: str,\n    qrels_path: str,\n    metrics: List[str] | None,\n    ks: List[int] | None,\n    save_path: str,\n)\n```\n\n**Function**\n- Performs IR retrieval metric evaluation based on `pytrec_eval`.\n- Reads standard TREC format:\n  - **qrels**: `<qid> <iter> <docid> <rel>`\n  - **run**: `<qid> Q0 <docid> <rank> <score> <tag>`\n- Supported Metrics: `mrr`, `map`, `recall@k`, `precision@k`, `ndcg@k`.\n- Automatically aggregates statistics and outputs as table.\n\n---\n\n## `evaluate_trec_pvalue`\n\n**Signature**\n```python\n@app.tool(\n    output=\"run_new_path,run_old_path,qrels_path,ir_metrics,ks,n_resamples,save_path->eval_res\"\n)\ndef evaluate_trec_pvalue(\n    run_new_path: str,\n    run_old_path: str,\n    qrels_path: str,\n    metrics: List[str] | None,\n    ks: List[int] | None,\n    n_resamples: int | None,\n    save_path: str,\n)\n```\n\n**Function**\n- Compares significance of two TREC result files using **Two-sided Permutation Test** to calculate p-value.\n- Default resampling count `n_resamples=10000`.\n- Outputs mean, difference, p-value, and significance flag.\n\n\n---\n\n## Configuration\n\n```yaml servers/evaluation/parameter.yaml icon=\"/images/yaml.svg\"\nsave_path: output/evaluate_results.json\n\n# QA task\nmetrics: [ 'acc', 'f1', 'em', 'coverem', 'stringem', 'rouge-1', 'rouge-2', 'rouge-l' ]\n\n# Retrieval task\nqrels_path: data/qrels.txt\nrun_path: data/run_a.txt\nks: [ 1, 5, 10, 20, 50, 100 ]\nir_metrics: [ \"mrr\", \"map\", \"recall\", \"ndcg\", \"precision\" ]\n\n# significant\nrun_new_path: data/run_a.txt\nrun_old_path: data/run_b.txt\nn_resamples: 10000\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `save_path` | str | Evaluation result save path (automatically appends timestamp) |\n| `metrics` | list[str] | Metric set used for QA / Generation tasks |\n| `qrels_path` | str | TREC format ground truth file path |\n| `run_path` | str | Result file for retrieval task |\n| `ks` | list[int] | Truncation levels for calculating NDCG@K, P@K, Recall@K, etc. |\n| `ir_metrics` | list[str] | Retrieval task metric names, supports `mrr`, `map`, `recall`, `ndcg`, `precision` |\n| `run_new_path` | str | Run file path generated by new model (significance analysis) |\n| `run_old_path` | str | Run file path of old model (significance analysis) |\n| `n_resamples` | int | Resampling count for Permutation Test |\n\n================\nFile: pages/en/api/generation.mdx\n================\n---\ntitle: \"Generation\"\nicon: \"pen-nib\"\n---\n\n## `generation_init`\n\n**Signature**\n```python\ndef generation_init(\n    backend_configs: Dict[str, Any],\n    sampling_params: Dict[str, Any],\n    extra_params: Optional[Dict[str, Any]] = None,\n    backend: str = \"vllm\",\n) -> None\n```\n\n**Function**\n- Initializes inference backend and sampling parameters.\n- Supports `vllm`, `openai`, `hf` backends.\n- `extra_params` can be used to pass `chat_template_kwargs` or other backend-specific parameters.\n\n---\n\n## `generate`\n\n**Signature**\n```python\nasync def generate(\n    prompt_ls: List[Union[str, Dict[str, Any]]],\n    system_prompt: str = \"\",\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Plain text conversation generation.\n- Automatically handles Prompt in list, supports string or OpenAI format dictionary.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"answer for prompt_0\", \"answer for prompt_1\", \"...\"]}\n```\n\n---\n\n## `multimodal_generate`\n\n**Signature**\n```python\nasync def multimodal_generate(\n    multimodal_path: List[List[str]],\n    prompt_ls: List[Union[str, Dict[str, Any]]],\n    system_prompt: str = \"\",\n    image_tag: Optional[str] = None,\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Text-image multimodal conversation generation.\n- `multimodal_path`: List of image paths corresponding to each Prompt (supports local path or URL).\n- `image_tag`: If specified (e.g., `<img>`), inserts image at that tag's position in Prompt; otherwise defaults to appending to end of Prompt.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"answer with images for prompt_0\", \"...\"]}\n```\n\n---\n\n## `multiturn_generate`\n\n**Signature**\n```python\nasync def multiturn_generate(\n    messages: List[Dict[str, str]],\n    system_prompt: str = \"\",\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Multi-turn conversation generation.\n- Supports only single-call generation, does not handle batch Prompts.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"assistant response\"]}\n```\n\n---\n\n## `vllm_shutdown`\n\n**Signature**\n```python\ndef vllm_shutdown() -> None\n```\n\n**Function**\n- Explicitly shuts down vLLM engine and releases VRAM resources.\n- Valid only when using `vllm` backend.\n\n---\n\n## Configuration\n\n```yaml servers/generation/parameter.yaml icon=\"/images/yaml.svg\"\n# servers/generation/parameter.yaml\nbackend: vllm # options: vllm, openai\nbackend_configs:\n  vllm:\n    model_name_or_path: openbmb/MiniCPM4-8B\n    gpu_ids: \"2,3\"\n    gpu_memory_utilization: 0.9\n    dtype: auto\n    trust_remote_code: true\n  openai:\n    model_name: MiniCPM4-8B\n    base_url: http://localhost:8000/v1\n    api_key: \"abc\"\n    concurrency: 8\n    retries: 3\n    base_delay: 1.0\n  hf:\n    model_name_or_path: openbmb/MiniCPM4-8B\n    gpu_ids: '2,3'\n    trust_remote_code: true\n    batch_size: 8\nsampling_params:\n  temperature: 0.7\n  top_p: 0.8\n  max_tokens: 2048\nextra_params:\n  chat_template_kwargs:\n    enable_thinking: false\nsystem_prompt: \"\"\nimage_tag: null\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `backend` | str | Specify generation backend, options `vllm`, `openai`, or `hf` (Transformers) |\n| `backend_configs` | dict | Model and runtime environment configuration for each backend |\n| `sampling_params` | dict | Sampling parameters to control generation diversity and length |\n| `extra_params` | dict | Extra parameters, e.g., `chat_template_kwargs` |\n| `system_prompt` | str | Global system prompt, added to context as `system` message |\n| `image_tag` | str | Image placeholder tag (if needed) |\n\n`backend_configs` Detailed Description:\n\n| Backend | Parameter | Description |\n|---|---|---|\n| **vllm** | `model_name_or_path` | Model name or path |\n|  | `gpu_ids` | GPU IDs used (e.g., `\"0,1\"`) |\n|  | `gpu_memory_utilization` | GPU memory utilization ratio (0–1) |\n|  | `dtype` | Data type (e.g., `auto`, `bfloat16`) |\n|  | `trust_remote_code` | Whether to trust remote code |\n| **openai** | `model_name` | OpenAI model name or self-hosted compatible model |\n|  | `base_url` | API base URL |\n|  | `api_key` | API Key |\n|  | `concurrency` | Max concurrent requests |\n|  | `retries` | API retry count |\n|  | `base_delay` | Base wait time for each retry (seconds) |\n| **hf** | `model_name_or_path` | Transformers model path |\n|  | `gpu_ids` | GPU IDs (same as above) |\n|  | `trust_remote_code` | Whether to trust remote code |\n|  | `batch_size` | Batch size per inference |\n\n`sampling_params` Detailed Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `temperature` | float | Controls randomness, higher means more diverse generation |\n| `top_p` | float | Nucleus sampling threshold |\n| `max_tokens` | int | Max generated tokens |\n\n================\nFile: pages/en/api/prompt.mdx\n================\n---\ntitle: \"Prompt\"\nicon: \"terminal\"\n---\n\n## QA Prompts\n\n### `qa_boxed`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,template->prompt_ls\")\ndef qa_boxed(\n    q_ls: List[str], \n    template: str | Path\n) -> List[PromptMessage]\n```\n\n**Function**\n\nBasic Q&A Prompt.\n\nLoads specified Jinja2 template, renders each question in the question list into a Prompt.\n\nTemplate Variable: `{{ question }}`\n\n### `qa_boxed_multiple_choice`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,choices_ls,template->prompt_ls\")\ndef qa_boxed_multiple_choice(\n    q_ls: List[str],\n    choices_ls: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nMultiple Choice Q&A Prompt.\n\nAutomatically formats choice list into \"A: ..., B: ...\" form and injects into template.\n\nTemplate Variables: `{{ question }}`, `{{ choices }}`\n\n### `qa_rag_boxed`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,template->prompt_ls\")\ndef qa_rag_boxed(\n    q_ls: List[str], \n    ret_psg: List[str | Any], \n    template: str | Path\n) -> list[PromptMessage]\n```\n\n**Function**\n\nStandard RAG Prompt.\n\nConcatenates retrieved passage lists and injects into template.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `qa_rag_boxed_multiple_choice`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,choices_ls,ret_psg,template->prompt_ls\")\ndef qa_rag_boxed_multiple_choice(\n    q_ls: List[str],\n    choices_ls: List[List[str]],\n    ret_psg: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nMultiple Choice Q&A Prompt with Retrieval Context.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`, `{{ choices }}`\n\n---\n\n## RankCoT Prompts\n\n### `RankCoT_kr`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,kr_template->prompt_ls\")\ndef RankCoT_kr(\n    q_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> list[PromptMessage]\n```\n\n**Function**\n\nRankCoT Phase 1: Knowledge Retrieval Prompt.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `RankCoT_qa`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,kr_ls,qa_template->prompt_ls\")\ndef RankCoT_qa(\n    q_ls: List[str],\n    kr_ls: List[str],\n    template: str | Path,\n) -> list[PromptMessage]\n```\n\n**Function**\n\nRankCoT Phase 2: Chain-of-Thought based Q&A Prompt.\n\nTemplate Variables: `{{ question }}`, `{{ CoT }}` (Here CoT is usually knowledge generated in the previous phase)\n\n---\n\n## IRCoT Prompts\n\n### `ircot_next_prompt`\n\n**Signature**\n\n```python\n@app.prompt(output=\"memory_q_ls,memory_ret_psg,template->prompt_ls\")\ndef ircot_next_prompt(\n    memory_q_ls: List[List[str | None]],\n    memory_ret_psg: List[List[List[str]] | None],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nIRCoT (Interleaved Retrieval CoT) Iterative Prompt Generation.\n\nConstructs next round's Prompt based on historical retrieval results and chain of thought. Supports single-turn and multi-turn history concatenation.\n\nTemplate Variables: `{{ documents }}`, `{{ question }}`, `{{ cur_answer }}`\n\n---\n\n## WebNote Prompts\n\n### `webnote_init_page`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,webnote_init_page_template->prompt_ls\")\ndef webnote_init_page(\n    q_ls: List[str],\n    plan_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Initialize note page.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`\n\n### `webnote_gen_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,webnote_gen_plan_template->prompt_ls\")\ndef webnote_gen_plan(\n    q_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate search plan.\n\nTemplate Variable: `{{ question }}`\n\n### `webnote_gen_subq`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,page_ls,webnote_gen_subq_template->prompt_ls\")\ndef webnote_gen_subq(\n    q_ls: List[str],\n    plan_ls: List[str],\n    page_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate sub-questions.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`, `{{ page }}`\n\n### `webnote_fill_page`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,page_ls,subq_ls,psg_ls,webnote_fill_page_template->prompt_ls\")\ndef webnote_fill_page(\n    q_ls: List[str],\n    plan_ls: List[str],\n    page_ls: List[str],\n    subq_ls: List[str],\n    psg_ls: List[Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Fill notes based on retrieval results.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`, `{{ sub_question }}`, `{{ docs_text }}`, `{{ page }}`\n\n### `webnote_gen_answer`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,page_ls,webnote_gen_answer_template->prompt_ls\")\ndef webnote_gen_answer(\n    q_ls: List[str],\n    page_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate final answer based on notes.\n\nTemplate Variables: `{{ question }}`, `{{ page }}`\n\n---\n\n## Search-R1 & R1-Searcher\n\n### `search_r1_gen`\n\n**Signature**\n\n```python\n@app.prompt(output=\"prompt_ls,ans_ls,ret_psg,search_r1_gen_template->prompt_ls\")\ndef search_r1_gen(\n    prompt_ls: List[PromptMessage],\n    ans_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nGeneration Prompt suitable for R1 style.\n\nTruncates Top-3 retrieval passages and injects into context.\n\nTemplate Variables: `{{ history }}`, `{{ answer }}`, `{{ passages }}`\n\n### `r1_searcher_gen`\n\n**Signature**\n\n```python\n@app.prompt(output=\"prompt_ls,ans_ls,ret_psg,r1_searcher_gen_template->prompt_ls\")\ndef r1_searcher_gen(\n    prompt_ls: List[PromptMessage],\n    ans_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nGeneration Prompt suitable for R1 Searcher.\n\nTruncates Top-5 retrieval passages.\n\nTemplate Variables: `{{ history }}`, `{{ answer }}`, `{{ passages }}`\n\n---\n\n## Search-o1 Prompts\n\n### `search_o1_init`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,searcho1_reasoning_template->prompt_ls\")\ndef search_o1_init(\n    q_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Initial Reasoning Prompt.\n\nTemplate Variable: `{{ question }}`\n\n### `search_o1_reasoning_indocument`\n\n**Signature**\n\n```python\n@app.prompt(output=\"extract_query_list,ret_psg,total_reason_list,searcho1_refine_template->prompt_ls\")\ndef search_o1_reasoning_indocument(\n    extract_query_list: List[str], \n    ret_psg: List[List[str]],       \n    total_reason_list: List[List[str]], \n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Reasoning Refinement Prompt.\n\nMerges historical reasoning steps (first + last 3 steps) with current retrieved documents for next step reasoning.\n\nTemplate Variables: `{{ prev_reasoning }}`, `{{ search_query }}`, `{{ document }}`\n\n### `search_o1_insert`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,total_subq_list,total_final_info_list,searcho1_reasoning_template->prompt_ls\") \ndef search_o1_insert(\n    q_ls: List[str],\n    total_subq_list: List[List[str]], \n    total_final_info_list: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Formatted Insertion Prompt.\n\nExplicitly inserts `<|begin_search_query|>` and search result tags into Prompt to construct complete chain-of-thought context.\n\n---\n\n## EVisRAG & Multi-branch Prompts\n\n### `gen_subq`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,gen_subq_template->prompt_ls\")\ndef gen_subq(\n    q_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nLoop/Branch Demo: Generate sub-questions based on documents.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `evisrag_vqa`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,evisrag_template->prompt_ls\")\ndef evisrag_vqa(\n    q_ls: List[str], \n    ret_psg: List[str | Any], \n    template: str | Path\n) -> list[PromptMessage]\n```\n\n**Function**\n\nMultimodal VQA RAG Prompt.\n\nAutomatically repeatedly inserts `<image>` Token into Prompt according to the number of retrieved images.\n\nTemplate Variable: `{{ question }}` (contains automatically injected image tokens)\n\n---\n\n## SurveyCPM Prompts\n\n### `surveycpm_search`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,cursor_ls,surveycpm_search_template->prompt_ls\")\ndef surveycpm_search(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    cursor_ls: List[str | None],\n    surveycpm_search_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Decide next search content.\n\nParses JSON format outline, generates text description of current outline.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_outline }}`, `{{ current_instruction }}`\n\n### `surveycpm_init_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,retrieved_info_ls,surveycpm_init_plan_template->prompt_ls\")\ndef surveycpm_init_plan(\n    instruction_ls: List[str],\n    retrieved_info_ls: List[str],\n    surveycpm_init_plan_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Initialize outline plan.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_information }}`\n\n### `surveycpm_write`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,cursor_ls,retrieved_info_ls,surveycpm_write_template->prompt_ls\")\ndef surveycpm_write(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    cursor_ls: List[str | None],\n    retrieved_info_ls: List[str],\n    surveycpm_write_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Write specific section content.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_survey }}`, `{{ current_instruction }}`, `{{ current_information }}`\n\n### `surveycpm_extend_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,surveycpm_extend_plan_template->prompt_ls\")\ndef surveycpm_extend_plan(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    surveycpm_extend_plan_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Extend or modify outline plan.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_survey }}`\n\n---\n\n## Configuration\n\n```yaml servers/prompt/parameter.yaml icon=\"/images/yaml.svg\"\n# QA\ntemplate: prompt/qa_boxed.jinja\n\n# RankCoT\nkr_template: prompt/RankCoT_knowledge_refinement.jinja\nqa_template: prompt/RankCoT_question_answering.jinja\n\n# Search-R1\nsearch_r1_gen_template: prompt/search_r1_append.jinja\n\n# R1-Searcher\nr1_searcher_gen_template: prompt/r1_searcher_append.jinja\n\n# Search-o1\nsearcho1_reasoning_template: prompt/search_o1_reasoning.jinja\nsearcho1_refine_template: prompt/search_o1_refinement.jinja\n\n\n# For other prompts, please add parameters here as needed\n\n# Take webnote as an example:\nwebnote_gen_plan_template: prompt/webnote_gen_plan.jinja\nwebnote_init_page_template: prompt/webnote_init_page.jinja\nwebnote_gen_subq_template: prompt/webnote_gen_subq.jinja\nwebnote_fill_page_template: prompt/webnote_fill_page.jinja\nwebnote_gen_answer_template: prompt/webnote_gen_answer.jinja\n\n# SurveyCPM\nsurveycpm_search_template: prompt/surveycpm_search.jinja\nsurveycpm_init_plan_template: prompt/surveycpm_init_plan.jinja\nsurveycpm_write_template: prompt/surveycpm_write.jinja\nsurveycpm_extend_plan_template: prompt/surveycpm_extend_plan.jinja\n```\n\n| Parameter | Description |\n|---|---|\n| `template` | Basic QA template path |\n| `kr_template` | RankCoT knowledge refinement template path |\n| `qa_template` | RankCoT Q&A template path |\n| `*_template` | Jinja2 template file path corresponding to each module function |\n\n================\nFile: pages/en/api/reranker.mdx\n================\n---\ntitle: \"Reranker\"\nicon: \"ranking-star\"\n---\n\n## `reranker_init`\n\n**Signature**\n```python\nasync def reranker_init(\n    model_name_or_path: str,\n    backend_configs: Dict[str, Any],\n    batch_size: int,\n    gpu_ids: Optional[object] = None,\n    backend: str = \"infinity\",\n) -> None\n```\n\n**Function**\n- Initializes reranker backend and model.\n\n---\n\n## `reranker_rerank`\n\n**Signature**\n```python\nasync def reranker_rerank(\n    query_list: List[str],\n    passages_list: List[List[str]],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n) -> Dict[str, List[Any]]\n```\n\n**Function**\n- **Reranks** candidate passages:\n\n**Output Format (JSON)**\n```json\n{\n  \"rerank_psg\": [\n    [\"best passage for q0\", \"...\"],\n    [\"best passage for q1\", \"...\"]\n  ]\n}\n```\n\n---\n\n## Configuration\n\n```yaml servers/reranker/parameter.yaml icon=\"/images/yaml.svg\"\nmodel_name_or_path: openbmb/MiniCPM-Reranker-Light\nbackend: sentence_transformers # options: infinity, sentence_transformers, openai\nbackend_configs:\n  infinity:\n    bettertransformer: false\n    pooling_method: auto\n    device: cuda\n    model_warmup: false\n    trust_remote_code: true\n  sentence_transformers:\n    device: cuda\n    trust_remote_code: true\n  openai:\n    model_name: text-embedding-3-small\n    base_url: \"https://api.openai.com/v1\"\n    api_key: \"\"\n\ngpu_ids: 0\ntop_k: 5\nbatch_size: 16\nquery_instruction: \"\"\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `model_name_or_path` | str | Model path or name (local or HuggingFace repo) |\n| `backend` | str | Select backend type: `infinity`, `sentence_transformers` or `openai` |\n| `backend_configs` | dict | Exclusive parameter settings for each backend |\n| `gpu_ids` | str/int | Specify GPU ID (can be multi-card, e.g., `\"0,1\"`) |\n| `top_k` | int | Number of reranked results returned |\n| `batch_size` | int | Sample quantity per batch |\n| `query_instruction` | str | Query prefix hint, used for prompt engineering or query modification |\n\n`backend_configs` Detailed Description:\n\n| Backend | Parameter | Description |\n|---|---|---|\n| **infinity** | `device` | Device type (cuda / cpu) |\n|  | `bettertransformer` | Whether to enable accelerated inference |\n|  | `pooling_method` | Vector pooling strategy |\n|  | `model_warmup` | Whether to warmup model |\n|  | `trust_remote_code` | Whether to trust remote code (Required for HuggingFace models) |\n| **sentence_transformers** | `device` | Device type (cuda / cpu) |\n|  | `trust_remote_code` | Whether to trust remote code |\n| **openai** | `model_name` | API Model name |\n|  | `base_url` | API access address |\n|  | `api_key` | OpenAI API Key |\n\n================\nFile: pages/en/api/retriever.mdx\n================\n---\ntitle: \"Retriever\"\nicon: \"magnifying-glass\"\n---\n\n## `retriever_init`\n\n**Signature**\n```python\nasync def retriever_init(\n    model_name_or_path: str,\n    backend_configs: Dict[str, Any],\n    batch_size: int,\n    corpus_path: str,\n    gpu_ids: Optional[object] = None,\n    is_multimodal: bool = False,\n    backend: str = \"sentence_transformers\",\n    index_backend: str = \"faiss\",\n    index_backend_configs: Optional[Dict[str, Any]] = None,\n    is_demo: bool = False,\n    collection_name: str = \"\",\n) -> None\n```\n\n**Function**\n- Initializes retrieval service.\n- Embedding Backend (backend): Responsible for converting text/images into vectors (Infinity, SentenceTransformers, OpenAI, BM25).\n- Index Backend (index_backend): Responsible for vector storage and retrieval (FAISS, Milvus, Qdrant).\n- Demo Mode: If is_demo=True, forces OpenAI + Milvus configuration, ignoring some parameters.\n\n---\n\n## `retriever_embed`\n\n**Signature**\n```python\nasync def retriever_embed(\n    embedding_path: Optional[str] = None,\n    overwrite: bool = False,\n    is_multimodal: bool = False,\n) -> None\n```\n\n**Function**\n- (Non-Demo Mode) Batches calculation of vector representations of corpus and saves as .npy file.\n- Only applies to Dense Retriever backend (BM25 not supported).\n\n\n---\n\n## `retriever_index`\n\n**Signature**\n```python\nasync def retriever_index(\n    embedding_path: str,\n    overwrite: bool = False,\n    collection_name: str = \"\",\n    corpus_path: str = \"\"\n) -> None\n```\n\n**Function**\n- Builds retrieval index.\n- FAISS: Reads embedding_path (.npy) to build local index file.\n- Milvus / Qdrant / Demo: Reads corpus_path (.jsonl), generates vectors and inserts into specified collection_name.\n\n---\n\n## `retriever_search`\n\n**Signature**\n```python\nasync def retriever_search(\n    query_list: List[str],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n    collection_name: str = \"\",\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Retrieves single or multiple queries.\n- Automatically handles query vectorization (adds query_instruction) and finds Top-K in specified collection_name (for Milvus or Qdrant) or default index.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_batch_search`\n\n**Signature**\n```python\nasync def retriever_batch_search(\n    batch_query_list: List[List[str]],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n    collection_name: str = \"\",\n) -> Dict[str, List[List[List[str]]]]\n```\n\n**Function**\n- Batch version of retriever_search, accepts nested list input.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg_ls\": [[[\"psg 1-1\"], [\"psg 1-2\"]], [[\"psg 2-1\"]]]}\n```\n\n---\n\n## `bm25_index`\n\n**Signature**\n```python\nasync def bm25_index(\n    overwrite: bool = False,\n) -> None\n```\n\n**Function**\n- When `backend=\"bm25\"`, builds BM25 sparse index and saves it.\n\n---\n\n## `bm25_search`\n\n**Signature**\n```python\nasync def bm25_search(\n    query_list: List[str],\n    top_k: int = 5,\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Keyword retrieval based on BM25 algorithm.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_deploy_search`\n\n**Signature**\n```python\nasync def retriever_deploy_search(\n    retriever_url: str,\n    query_list: List[str],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- As a client, calls remote retrieval service deployed at retriever_url for query.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_websearch`\n\n**Signature**\n```python\nasync def retriever_websearch(\n    query_list: List[str],\n    top_k: Optional[int] | None = 5,\n    retrieve_thread_num: Optional[int] | None = 1,\n    websearch_backend: str = \"tavily\",\n    websearch_backend_configs: Optional[Dict[str, Any]] | None = None,\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Unified Web retrieval tool. Choose backend via `websearch_backend` (`tavily`, `exa`, `zhipuai`) and configure in `websearch_backend_configs`.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"snippet 1\", \"snippet 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_batch_websearch`\n\n**Signature**\n```python\nasync def retriever_batch_websearch(\n    batch_query_list: List[List[str]],\n    top_k: Optional[int] | None = 5,\n    retrieve_thread_num: Optional[int] | None = 1,\n    websearch_backend: str = \"tavily\",\n    websearch_backend_configs: Optional[Dict[str, Any]] | None = None,\n) -> Dict[str, List[List[List[str]]]]\n```\n\n**Function**\n- Batch Web retrieval for SurveyCPM-style pipelines.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg_ls\": [[[\"snippet 1\", \"snippet 2\"]], [\"...\" ]]} \n```\n\n\n---\n\n## Configuration\n\n```yaml servers/retriever/parameter.yaml icon=\"/images/yaml.svg\"\nmodel_name_or_path: openbmb/MiniCPM-Embedding-Light\ncorpus_path: data/corpus_example.jsonl\nembedding_path: embedding/embedding.npy\ncollection_name: wiki\n\n# Embedding Backend Configuration\nbackend: sentence_transformers # options: infinity, sentence_transformers, openai, bm25\nbackend_configs:\n  infinity:\n    bettertransformer: false\n    pooling_method: auto\n    model_warmup: false\n    trust_remote_code: true\n  sentence_transformers:\n    trust_remote_code: true\n    sentence_transformers_encode:\n      normalize_embeddings: false\n      encode_chunk_size: 256\n      q_prompt_name: query\n      psg_prompt_name: document\n      psg_task: null\n      q_task: null\n  openai:\n    model_name: text-embedding-3-small\n    base_url: \"https://api.openai.com/v1\"\n    api_key: \"abc\"\n  bm25:\n    lang: en\n    save_path: index/bm25\n\n# Index Backend Configuration\nindex_backend: faiss # options: faiss, milvus, qdrant\nindex_backend_configs:\n  faiss:\n    index_use_gpu: True\n    index_chunk_size: 10000\n    index_path: index/index.index\n  milvus:\n    uri: index/milvus_demo.db # Local file for Lite, or http://host:port\n    token: null\n    id_field_name: id\n    vector_field_name: vector\n    text_field_name: contents\n    index_params:\n      index_type: AUTOINDEX\n      metric_type: IP\n\n# Websearch Backend Configuration\nwebsearch_backend: tavily # options: tavily, exa, zhipuai\nwebsearch_backend_configs:\n  exa:\n    api_key: \"\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n  tavily:\n    api_key: \"\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n  zhipuai:\n    api_key: \"\"\n    base_url: \"https://open.bigmodel.cn/api/paas/v4/web_search\"\n    search_engine: \"search_std\"\n    search_intent: false\n    search_recency_filter: \"noLimit\"\n    content_size: \"medium\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n\nbatch_size: 16\ntop_k: 5\ngpu_ids: \"1\"\nquery_instruction: \"\"\nis_multimodal: false\noverwrite: false\nretrieve_thread_num: 1\nretriever_url: \"http://127.0.0.1:64501\"\nis_demo: false\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `model_name_or_path` | str | Retrieval model path or name (e.g., HuggingFace model ID) |\n| `corpus_path` | str | Input corpus JSONL file path |\n| `embedding_path` | str | Vector file save path (`.npy`) |\n| `collection_name` | str | Milvus or Qdrant collection name |\n| `backend` | str | Select retrieval backend: `infinity`, `sentence_transformers`, `openai`, `bm25` |\n| `index_backend` | str | Index backend: `faiss`, `milvus`, `qdrant` |\n| `backend_configs` | dict | Parameter configuration for each backend (see table below) |\n| `index_backend_configs` | dict | Parameter configuration for each index backend (see table below) |\n| `websearch_backend` | str | Web search backend: `tavily`, `exa`, `zhipuai` |\n| `websearch_backend_configs` | dict | Web search backend configs (api_key, retries, payload, etc.) |\n| `batch_size` | int | Batch size for vector generation or retrieval |\n| `top_k` | int | Number of returned candidate passages |\n| `gpu_ids` | str | Specify visible GPU devices, e.g., `\"0,1\"` |\n| `query_instruction` | str | Query prefix (used by instruction-tuning models) |\n| `is_multimodal` | bool | Whether to enable multimodal embedding (e.g., image) |\n| `overwrite` | bool | Whether to overwrite if embedding or index file already exists |\n| `retrieve_thread_num` | int | Concurrent thread number for Web retrieval |\n| `retriever_url` | str | URL of deployed retriever server |\n| `is_demo` | bool | Demo mode s","isInternal":false,"tokens":13212,"sizeBytes":50018},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/OpenBMB/UltraRAG/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# AGENTS.md\n\nThis document is the primary engineering guide for autonomous coding agents working in the `UltraRAG` repository.\n\nUse this file as the source of truth for architecture, conventions, workflows, and safe change patterns.\nIf `CLAUDE.md` exists, it should only point to this file.\n\n---\n\n## 1) Project Identity\n\n`UltraRAG` is a lightweight RAG framework built around the Model Context Protocol (MCP).\nThe key design choice is strict modularization: retrieval, prompting, generation, routing, memory, and evaluation are implemented as independent MCP servers orchestrated by YAML pipelines.\n\nCurrent core metadata:\n\n- Package: `ultrarag`\n- Version: `0.3.0`\n- Python: `>=3.11, <3.13`\n- CLI entrypoint: `ultrarag = ultrarag.client:main`\n- Package manager: `uv` (`[tool.uv] package = true`)\n\n---\n\n## 2) Repository Map (What Matters Most)\n\n```text\nUltraRAG/\n├── src/ultrarag/                    # Installable core package\n│   ├── client.py                    # CLI + pipeline engine + run/build orchestration\n│   ├── server.py                    # UltraRAG_MCP_Server (FastMCP extension)\n│   ├── api.py                       # Python API wrappers (ToolCall, PipelineCall)\n│   ├── cli.py                       # Rich banner and CLI visuals\n│   ├── mcp_logging.py               # Central logging setup\n│   ├── mcp_exceptions.py            # Node.js checks for remote MCP\n│   └── utils.py                     # Subprocess lifecycle helpers\n│\n├── servers/                         # MCP microservices (each server is independent)\n│   ├── retriever/\n│   ├── generation/\n│   ├── prompt/\n│   ├── reranker/\n│   ├── benchmark/\n│   ├── evaluation/\n│   ├── corpus/\n│   ├── memory/\n│   ├── router/\n│   ├── custom/\n│   ├── pageindex/\n│   └── sayhello/\n│\n├── examples/\n│   ├── demos/                       # UI-ready demo pipelines\n│   └── experiments/                 # Experiment/research pipelines\n│\n├── ui/\n│   ├── backend/                     # Flask backend + pipeline manager\n│   └── frontend/                    # Vite + React + TypeScript\n│\n├── docs/                            # Docs and assets\n├── script/                          # Utility scripts (deploy, case study, etc.)\n├── pyproject.toml                   # Dependencies + package metadata\n├── uv.lock                          # Locked dependency graph\n├── Dockerfile*                      # Container variants\n└── .gitignore\n```\n\nImportant generated/derived files:\n\n- `servers/*/server.yaml` (generated by each server `build` tool)\n- `examples/**/parameter/*_parameter.yaml` (pipeline-merged parameters)\n- `examples/**/server/*_server.yaml` (pipeline-merged server config)\n- `output/memory_*.json` (per-run memory snapshots)\n\n---\n\n## 3) Mental Model of the System\n\nThink of UltraRAG as a three-layer system:\n\n1. **Interface layer**: CLI (`ultrarag ...`), UI (`ultrarag show ui`), and Python API (`ToolCall`, `PipelineCall`)\n2. **Orchestration layer**: `src/ultrarag/client.py` (`build`, `load_pipeline_context`, `execute_pipeline`)\n3. **Execution layer**: MCP servers in `servers/*`, each exposing tools/prompts over stdio (or remote MCP proxy)\n\nThe runtime contract is:\n\n- A pipeline YAML declares **which servers** to use and **which steps** to execute.\n- The client resolves I/O dependencies between steps.\n- Each step calls exactly one MCP tool or prompt.\n- Outputs are saved to a shared variable pool and can feed downstream steps.\n\n---\n\n## 4) Two-Phase Execution Lifecycle\n\n### Phase A: Build\n\nCommand:\n\n```bash\nultrarag build <pipeline.yaml>\n```\n\nWhat happens:\n\n- Reads `servers:` from the pipeline YAML.\n- For each referenced server, calls the server's `build` tool.\n- Produces:\n  - `<pipeline_dir>/parameter/<pipeline_name>_parameter.yaml`\n  - `<pipeline_dir>/server/<pipeline_name>_server.yaml`\n\nWhy this matters:\n\n- `build` materializes exact tool/prompt I/O metadata before runtime.\n- UI and runner rely on these generated artifacts for deterministic execution.\n\n### Phase B: Run\n\nCommand:\n\n```bash\nultrarag run <pipeline.yaml> [--param path] [--is_demo]\n```\n\nWhat happens:\n\n- Loads generated server config + parameter config.\n- Creates `fastmcp.Client` transport config for each server.\n- Executes pipeline steps in order, including `loop` and `branch`.\n- Saves intermediate memory snapshots and writes `output/memory_*.json`.\n- Invokes cleanup tools (e.g., tools ending with `vllm_shutdown`) if present.\n\n---\n\n## 5) Pipeline DSL Reference\n\nUltraRAG accepts mixed step forms:\n\n### 5.1 Plain step\n\n```yaml\n- retriever.retriever_search\n```\n\n### 5.2 Step with input/output remapping\n\n```yaml\n- generation.generate:\n    input:\n      prompt_ls: custom_prompt_ls\n    output:\n      ans_ls: final_answer_ls\n```\n\n### 5.3 Loop block\n\n```yaml\n- loop:\n    times: 3\n    steps:\n    - retriever.retriever_search\n    - generation.generate\n```\n\n### 5.4 Branch block\n\n```yaml\n- branch:\n    router:\n    - router.route_query\n    branches:\n      need_retrieval:\n      - retriever.retriever_search\n      direct_answer:\n      - generation.generate\n```\n\n### 5.5 Prompt vs Tool step semantics\n\n- Steps under the `prompt` server call `client.get_prompt(...)`.\n- Non-prompt steps call `client.call_tool(...)`.\n\n---\n\n## 6) Variable Resolution and Data Flow Rules\n\nIn `UltraData`, each tool input value is interpreted by convention:\n\n- `\"$foo\"` -> load from server-local params (`parameter.yaml`)\n- `\"bar\"`  -> read from global variable pool (`global_vars[\"bar\"]`)\n- `\"memory_xxx\"` -> read/write memory lists\n\nOutput handling:\n\n- Tool returns JSON payload -> keys mapped to declared outputs\n- Output remapping (`output:` in pipeline step) is applied at save time\n- Prompt outputs usually produce `prompt_ls`\n\nBranch handling:\n\n- Branch-aware values use wrapped list records with branch-state keys.\n- Internal sentinel (`UNSET`) is used to distinguish \"not filled yet\" from `None`.\n\nMemory handling:\n\n- The engine tracks `memory_*` histories automatically.\n- Final snapshots are serialized to `output/memory_<...>.json`.\n- If a memory server is detected, turn-level memory auto-save can be triggered.\n\n---\n\n## 7) Core Python Modules (Authoritative Guide)\n\n### `src/ultrarag/client.py`\n\nThis is the orchestration heart of the project.\n\nKey responsibilities:\n\n- CLI entrypoint (`main`)\n- UI launch (`launch_ui`) and case-study launch (`launch_case_study`)\n- Config loading (`Configuration`)\n- Pipeline data graph and state (`UltraData`)\n- Build pipeline configs (`build`)\n- Load runtime context (`load_pipeline_context`)\n- Execute step engine (`execute_pipeline`)\n- Run full pipeline (`run`)\n\nImportant runtime behaviors:\n\n- Supports both local python MCP servers (`path.endswith(\".py\")`) and remote MCP endpoints (`http(s)`).\n- For remote MCP, requires Node.js >= 20 and uses `npx -y mcp-remote <url>`.\n- Keeps loop-termination state in `ContextVar` for coroutine safety.\n- Emits structured stream events in demo/UI flows (`step_start`, `step_end`, `token`, `sources`).\n\n### `src/ultrarag/server.py`\n\nDefines `UltraRAG_MCP_Server`, a compatibility wrapper over FastMCP.\n\nKey responsibilities:\n\n- Enhanced `tool()` and `prompt()` registration with `output` metadata support\n- Metadata capture for automatic config generation\n- `build(parameter_file)` to generate per-server `server.yaml`\n- Compatibility filtering for FastMCP signature differences\n\n### `src/ultrarag/api.py`\n\nProvides ergonomic Python-side wrappers:\n\n- `initialize(servers, server_root, log_level)`\n- `ToolCall.server_name.tool_name(...)`\n- `PipelineCall(pipeline_file, parameter_file, log_level)`\n\n### `src/ultrarag/mcp_logging.py`\n\n- Initializes root logger `UltraRAG`\n- Rich console logging + file logging (`logs/<timestamp>.log`)\n- Log level controlled by `log_level` argument and environment\n\n### `src/ultrarag/mcp_exceptions.py`\n\n- Validates local Node.js availability/version\n- Raises `NodeNotInstalledError` / `NodeVersionTooLowError`\n\n### `src/ultrarag/utils.py`\n\n- Subprocess lifecycle helpers\n- POSIX parent-death signal support\n- Windows job object support for child-process cleanup\n\n---\n\n## 8) MCP Server Authoring Contract\n\nEach server follows this shape:\n\n```text\nservers/<name>/\n├── parameter.yaml\n├── server.yaml            # generated\n└── src/<name>.py\n```\n\n### 8.1 Registration styles\n\nUse either:\n\n1. Decorator style\n2. Class-bound method registration style\n\nBoth are valid in this codebase.\n\n### 8.2 `output=` grammar\n\nCanonical form:\n\n```text\ninput1,input2,$param_a -> output1,output2\n```\n\nRules:\n\n- Left side maps function args to pipeline inputs.\n- Right side defines expected output keys from returned dict/JSON.\n- `-> None` means no output variables.\n- `$param` means value comes from server parameter file.\n\n### 8.3 Return payload expectations\n\n- For tools: return JSON-serializable dict payloads matching declared output keys.\n- For prompts: return prompt messages (typically list-like prompt payloads consumed by `get_prompt`).\n\n### 8.4 Entrypoint requirement\n\nServer modules should end with:\n\n```python\nif __name__ == \"__main__\":\n    app.run(transport=\"stdio\")\n```\n\n---\n\n## 9) Retriever/Generation/Prompt Specific Notes\n\n### Retriever (`servers/retriever`)\n\n- Supports multiple retrieval modes:\n  - Dense retrieval\n  - BM25\n  - Web search\n  - Project-memory retrieval\n- Index backends are pluggable via factory:\n  - `faiss`\n  - `milvus`\n  - `qdrant`\n- Web search backends are pluggable via factory:\n  - `exa`\n  - `tavily`\n  - `zhipuai`\n  - `serply`\n\n### Generation (`servers/generation`)\n\n- Supports generation backends including `openai`, `vllm`, and hf workflows.\n- Provides explicit cleanup tool `vllm_shutdown`.\n- Demo mode can use local streaming generation service.\n\n### Prompt (`servers/prompt`)\n\n- Uses `SandboxedEnvironment` from Jinja2 for safer rendering.\n- Validates template paths to reduce traversal risk.\n- Escapes string inputs before template rendering.\n\n---\n\n## 10) UI Backend Architecture (`ui/backend`)\n\n### `app.py`\n\n- Flask app factory (`create_app`)\n- Serves frontend static assets\n- Exposes chat/pipeline/KB/auth endpoints\n- Reads optional frontend override via `ULTRARAG_FRONTEND_DIR`\n\n### `pipeline_manager.py`\n\nThis module is large and central to UI behavior.\n\nMajor responsibilities:\n\n- Session lifecycle and streaming chat management\n- Background chat task management\n- Pipeline CRUD (list/load/save/rename/delete)\n- Parameter load/save/build wrappers\n- Knowledge base file ingest and pipeline triggering\n- Memory synchronization to per-user KB collections\n- Optional server introspection via AST stub generation if `server.yaml` is missing\n\nNotable behavior:\n\n- Applies defensive patches to suppress noisy closed-event-loop teardown logs.\n- Uses a queue bridge for async-to-sync SSE event streaming.\n- Supports automatic memory-to-KB sync for pipelines that include memory components.\n\n---\n\n## 11) Storage Model and Paths\n\nDefault UI storage root:\n\n- `ui/storage`\n\nCan be overridden by:\n\n- `ULTRARAG_UI_STORAGE_ROOT`\n\nKey subpaths:\n\n- `db/users.sqlite3`\n- `chat_sessions/`\n- `knowledge_base/raw|corpus|chunks|index`\n- `memory/`\n- `knowledge_base/_memory_sync`\n\nRuntime outputs:\n\n- `output/memory_*.json`\n- `logs/*.log`\n\n---\n\n## 12) Environment Variables You Should Know\n\n- `ULTRARAG_UI_STORAGE_ROOT`: override UI storage root\n- `ULTRARAG_FRONTEND_DIR`: override frontend static directory\n- `ULTRARAG_SESSION_TIMEOUT`: foreground chat session timeout\n- `ULTRARAG_BG_SESSION_TIMEOUT`: background session timeout\n- `ULTRARAG_LOG_TS`: custom timestamp seed for log file naming\n- `log_level`: consumed by core logger initialization\n\n---\n\n## 13) Dependency Model\n\nInstall tiers from `pyproject.toml`:\n\n- Core install: no extras\n- `retriever` extra\n- `generation` extra\n- `evaluation` extra\n- `corpus` extra\n- `all` extra (union)\n\nTypical commands:\n\n```bash\nuv sync\nuv sync --extra retriever\nuv sync --extra generation\nuv sync --all-extras\n```\n\nDevelopment dependencies include:\n\n- `ruff`\n- `ipython`\n- `jupyter`\n- `pytest`\n\n---\n\n## 14) CLI Commands (Canonical)\n\n```bash\nultrarag build <pipeline.yaml>\nultrarag run <pipeline.yaml> [--param <parameter.yaml>] [--log_level info|debug|warn|error] [--is_demo]\nultrarag show ui [--host 127.0.0.1] [--port 5050]\nultrarag show case [--config_path <memory.json>] [--host 127.0.0.1] [--port 8080]\n```\n\nMinimal smoke check:\n\n```bash\nultrarag run examples/experiments/sayhello.yaml\n```\n\n---\n\n## 15) Docker Variants\n\n- `Dockerfile`: full image (builds frontend, installs all extras)\n- `Dockerfile.base-cpu`: CPU base image\n- `Dockerfile.base-gpu`: GPU base image\n\nAll variants start UI with:\n\n```bash\nultrarag show ui --port 5050 --host 0.0.0.0\n```\n\n---\n\n## 16) Development Playbooks\n\n### 16.1 Add a new MCP server\n\n1. Create `servers/<name>/parameter.yaml`\n2. Implement `servers/<name>/src/<name>.py`\n3. Register tools/prompts via `app.tool` / `app.prompt` (or class-bound registration)\n4. Ensure `app.run(transport=\"stdio\")` exists\n5. Add server to `servers:` in a pipeline YAML\n6. Run `ultrarag build <pipeline.yaml>`\n\n### 16.2 Add a new tool to an existing server\n\n1. Implement function/method\n2. Register with explicit `output=...` contract\n3. Ensure return payload keys match outputs\n4. Update relevant parameter keys in `parameter.yaml` if using `$...`\n5. Add the step in pipeline YAML and rebuild\n\n### 16.3 Add a retriever index backend\n\n1. Implement backend in `servers/retriever/src/index_backends/`\n2. Follow `BaseIndexBackend` contract\n3. Register in `_INDEX_BACKENDS` map in `index_backends/__init__.py`\n\n### 16.4 Add a web-search backend\n\n1. Implement backend in `servers/retriever/src/websearch_backends/`\n2. Follow `BaseWebSearchBackend` contract\n3. Register in `_WEBSEARCH_BACKENDS` map\n\n### 16.5 Modify UI pipeline behavior\n\nPrimary files:\n\n- `ui/backend/app.py`\n- `ui/backend/pipeline_manager.py`\n\nIf touching build/runtime semantics, cross-check against:\n\n- `src/ultrarag/client.py`\n\n---\n\n## 17) Coding Standards (Repository-Conformant)\n\n- Use type hints for function signatures.\n- Prefer `pathlib.Path` for filesystem paths.\n- Use `yaml.safe_load` / `yaml.safe_dump`.\n- Use project logger (`get_logger` or `app.logger`) instead of `print`.\n- Keep tool outputs deterministic and JSON-serializable.\n- Keep imports grouped: stdlib -> third-party -> local.\n- Keep async boundaries explicit (`async`/`await`).\n\n---\n\n## 18) What Not To Edit Blindly\n\nTreat these as generated or runtime artifacts unless intentionally regenerating:\n\n- `servers/*/server.yaml`\n- `examples/**/parameter/*_parameter.yaml`\n- `examples/**/server/*_server.yaml`\n- `output/*`\n- `logs/*`\n- `ui/storage/*` runtime data\n\nAlso avoid committing secrets:\n\n- `.env`\n- any credential-bearing local config\n\n---\n\n## 19) Common Failure Modes and Fixes\n\n### Error: server file not found\n\n- Check `servers.<name>` path in pipeline YAML.\n- Ensure server entry script exists at `servers/<name>/src/<name>.py` (or update path in config).\n\n### Error: missing variable in pipeline execution\n\n- Verify output key names from upstream tool match downstream input names.\n- Verify remapping under step-level `output:` is correct.\n- Verify `$param` keys exist in that server's parameter config.\n\n### Remote MCP server fails to start\n\n- Ensure Node.js >= 20.\n- Confirm `npx` is available.\n- Confirm remote URL in server path is reachable.\n\n### Build succeeds but UI cannot list tools\n\n- Ensure `server.yaml` exists or can be inferred by AST parsing.\n- Check for unusual dynamic registration patterns that static analysis cannot infer.\n\n### No final answer in chat\n\n- Inspect `output/memory_*.json`.\n- Check whether generation step ran and produced `ans_ls`.\n- Check stream events in UI path (`step_start`, `step_end`, `sources`, `token`, `final`).\n\n---\n\n## 20) Validation Checklist for Agents\n\nBefore finalizing any non-trivial change:\n\n1. Build the affected pipeline:\n   - `ultrarag build <pipeline.yaml>`\n2. Run a relevant smoke case:\n   - `ultrarag run <pipeline.yaml>`\n3. If UI behavior changed:\n   - run `ultrarag show ui` and verify route behavior\n4. If retriever/generation backends changed:\n   - validate parameter schema keys and output names\n5. Keep generated artifacts intentional:\n   - do not accidentally commit transient runtime outputs\n\n---\n\n## 21) Minimal Quickstart for New Agents\n\n```bash\n# 1) Install dependencies\nuv sync --all-extras\n\n# 2) Smoke test\nultrarag run examples/experiments/sayhello.yaml\n\n# 3) Build and run a demo pipeline\nultrarag build examples/demos/LLM.yaml\nultrarag run examples/demos/LLM.yaml\n\n# 4) Launch UI\nultrarag show ui --host 127.0.0.1 --port 5050\n```\n\n---\n\n## 22) Final Notes\n\n- This repository is orchestration-first: correctness depends heavily on I/O naming consistency across tools and pipeline steps.\n- Most regressions come from mismatched variable names, stale generated configs, or incomplete parameter updates.\n- When in doubt, inspect `src/ultrarag/client.py` first: it is the execution truth.\n","isInternal":false,"tokens":4141,"sizeBytes":17082}],"systemPromptSnippet":"<yakaai_skills repo=\"OpenBMB/UltraRAG\">\n<!-- File: AGENTS.md (Tokens: ~4141 | Category: root-instruction) -->\n# AGENTS.md\n\nThis document is the primary engineering guide for autonomous coding agents working in the `UltraRAG` repository.\n\nUse this file as the source of truth for architecture, conventions, workflows, and safe change patterns.\nIf `CLAUDE.md` exists, it should only point to this file.\n\n---\n\n## 1) Project Identity\n\n`UltraRAG` is a lightweight RAG framework built around the Model Context Protocol (MCP).\nThe key design choice is strict modularization: retrieval, prompting, generation, routing, memory, and evaluation are implemented as independent MCP servers orchestrated by YAML pipelines.\n\nCurrent core metadata:\n\n- Package: `ultrarag`\n- Version: `0.3.0`\n- Python: `>=3.11, <3.13`\n- CLI entrypoint: `ultrarag = ultrarag.client:main`\n- Package manager: `uv` (`[tool.uv] package = true`)\n\n---\n\n## 2) Repository Map (What Matters Most)\n\n```text\nUltraRAG/\n├── src/ultrarag/                    # Installable core package\n│   ├── client.py                    # CLI + pipeline engine + run/build orchestration\n│   ├── server.py                    # UltraRAG_MCP_Server (FastMCP extension)\n│   ├── api.py                       # Python API wrappers (ToolCall, PipelineCall)\n│   ├── cli.py                       # Rich banner and CLI visuals\n│   ├── mcp_logging.py               # Central logging setup\n│   ├── mcp_exceptions.py            # Node.js checks for remote MCP\n│   └── utils.py                     # Subprocess lifecycle helpers\n│\n├── servers/                         # MCP microservices (each server is independent)\n│   ├── retriever/\n│   ├── generation/\n│   ├── prompt/\n│   ├── reranker/\n│   ├── benchmark/\n│   ├── evaluation/\n│   ├── corpus/\n│   ├── memory/\n│   ├── router/\n│   ├── custom/\n│   ├── pageindex/\n│   └── sayhello/\n│\n├── examples/\n│   ├── demos/                       # UI-ready demo pipelines\n│   └── experiments/                 # Experiment/research pipelines\n│\n├── ui/\n│   ├── backend/                     # Flask backend + pipeline manager\n│   └── frontend/                    # Vite + React + TypeScript\n│\n├── docs/                            # Docs and assets\n├── script/                          # Utility scripts (deploy, case study, etc.)\n├── pyproject.toml                   # Dependencies + package metadata\n├── uv.lock                          # Locked dependency graph\n├── Dockerfile*                      # Container variants\n└── .gitignore\n```\n\nImportant generated/derived files:\n\n- `servers/*/server.yaml` (generated by each server `build` tool)\n- `examples/**/parameter/*_parameter.yaml` (pipeline-merged parameters)\n- `examples/**/server/*_server.yaml` (pipeline-merged server config)\n- `output/memory_*.json` (per-run memory snapshots)\n\n---\n\n## 3) Mental Model of the System\n\nThink of UltraRAG as a three-layer system:\n\n1. **Interface layer**: CLI (`ultrarag ...`), UI (`ultrarag show ui`), and Python API (`ToolCall`, `PipelineCall`)\n2. **Orchestration layer**: `src/ultrarag/client.py` (`build`, `load_pipeline_context`, `execute_pipeline`)\n3. **Execution layer**: MCP servers in `servers/*`, each exposing tools/prompts over stdio (or remote MCP proxy)\n\nThe runtime contract is:\n\n- A pipeline YAML declares **which servers** to use and **which steps** to execute.\n- The client resolves I/O dependencies between steps.\n- Each step calls exactly one MCP tool or prompt.\n- Outputs are saved to a shared variable pool and can feed downstream steps.\n\n---\n\n## 4) Two-Phase Execution Lifecycle\n\n### Phase A: Build\n\nCommand:\n\n```bash\nultrarag build <pipeline.yaml>\n```\n\nWhat happens:\n\n- Reads `servers:` from the pipeline YAML.\n- For each referenced server, calls the server's `build` tool.\n- Produces:\n  - `<pipeline_dir>/parameter/<pipeline_name>_parameter.yaml`\n  - `<pipeline_dir>/server/<pipeline_name>_server.yaml`\n\nWhy this matters:\n\n- `build` materializes exact tool/prompt I/O metadata before runtime.\n- UI and runner rely on these generated artifacts for deterministic execution.\n\n### Phase B: Run\n\nCommand:\n\n```bash\nultrarag run <pipeline.yaml> [--param path] [--is_demo]\n```\n\nWhat happens:\n\n- Loads generated server config + parameter config.\n- Creates `fastmcp.Client` transport config for each server.\n- Executes pipeline steps in order, including `loop` and `branch`.\n- Saves intermediate memory snapshots and writes `output/memory_*.json`.\n- Invokes cleanup tools (e.g., tools ending with `vllm_shutdown`) if present.\n\n---\n\n## 5) Pipeline DSL Reference\n\nUltraRAG accepts mixed step forms:\n\n### 5.1 Plain step\n\n```yaml\n- retriever.retriever_search\n```\n\n### 5.2 Step with input/output remapping\n\n```yaml\n- generation.generate:\n    input:\n      prompt_ls: custom_prompt_ls\n    output:\n      ans_ls: final_answer_ls\n```\n\n### 5.3 Loop block\n\n```yaml\n- loop:\n    times: 3\n    steps:\n    - retriever.retriever_search\n    - generation.generate\n```\n\n### 5.4 Branch block\n\n```yaml\n- branch:\n    router:\n    - router.route_query\n    branches:\n      need_retrieval:\n      - retriever.retriever_search\n      direct_answer:\n      - generation.generate\n```\n\n### 5.5 Prompt vs Tool step semantics\n\n- Steps under the `prompt` server call `client.get_prompt(...)`.\n- Non-prompt steps call `client.call_tool(...)`.\n\n---\n\n## 6) Variable Resolution and Data Flow Rules\n\nIn `UltraData`, each tool input value is interpreted by convention:\n\n- `\"$foo\"` -> load from server-local params (`parameter.yaml`)\n- `\"bar\"`  -> read from global variable pool (`global_vars[\"bar\"]`)\n- `\"memory_xxx\"` -> read/write memory lists\n\nOutput handling:\n\n- Tool returns JSON payload -> keys mapped to declared outputs\n- Output remapping (`output:` in pipeline step) is applied at save time\n- Prompt outputs usually produce `prompt_ls`\n\nBranch handling:\n\n- Branch-aware values use wrapped list records with branch-state keys.\n- Internal sentinel (`UNSET`) is used to distinguish \"not filled yet\" from `None`.\n\nMemory handling:\n\n- The engine tracks `memory_*` histories automatically.\n- Final snapshots are serialized to `output/memory_<...>.json`.\n- If a memory server is detected, turn-level memory auto-save can be triggered.\n\n---\n\n## 7) Core Python Modules (Authoritative Guide)\n\n### `src/ultrarag/client.py`\n\nThis is the orchestration heart of the project.\n\nKey responsibilities:\n\n- CLI entrypoint (`main`)\n- UI launch (`launch_ui`) and case-study launch (`launch_case_study`)\n- Config loading (`Configuration`)\n- Pipeline data graph and state (`UltraData`)\n- Build pipeline configs (`build`)\n- Load runtime context (`load_pipeline_context`)\n- Execute step engine (`execute_pipeline`)\n- Run full pipeline (`run`)\n\nImportant runtime behaviors:\n\n- Supports both local python MCP servers (`path.endswith(\".py\")`) and remote MCP endpoints (`http(s)`).\n- For remote MCP, requires Node.js >= 20 and uses `npx -y mcp-remote <url>`.\n- Keeps loop-termination state in `ContextVar` for coroutine safety.\n- Emits structured stream events in demo/UI flows (`step_start`, `step_end`, `token`, `sources`).\n\n### `src/ultrarag/server.py`\n\nDefines `UltraRAG_MCP_Server`, a compatibility wrapper over FastMCP.\n\nKey responsibilities:\n\n- Enhanced `tool()` and `prompt()` registration with `output` metadata support\n- Metadata capture for automatic config generation\n- `build(parameter_file)` to generate per-server `server.yaml`\n- Compatibility filtering for FastMCP signature differences\n\n### `src/ultrarag/api.py`\n\nProvides ergonomic Python-side wrappers:\n\n- `initialize(servers, server_root, log_level)`\n- `ToolCall.server_name.tool_name(...)`\n- `PipelineCall(pipeline_file, parameter_file, log_level)`\n\n### `src/ultrarag/mcp_logging.py`\n\n- Initializes root logger `UltraRAG`\n- Rich console logging + file logging (`logs/<timestamp>.log`)\n- Log level controlled by `log_level` argument and environment\n\n### `src/ultrarag/mcp_exceptions.py`\n\n- Validates local Node.js availability/version\n- Raises `NodeNotInstalledError` / `NodeVersionTooLowError`\n\n### `src/ultrarag/utils.py`\n\n- Subprocess lifecycle helpers\n- POSIX parent-death signal support\n- Windows job object support for child-process cleanup\n\n---\n\n## 8) MCP Server Authoring Contract\n\nEach server follows this shape:\n\n```text\nservers/<name>/\n├── parameter.yaml\n├── server.yaml            # generated\n└── src/<name>.py\n```\n\n### 8.1 Registration styles\n\nUse either:\n\n1. Decorator style\n2. Class-bound method registration style\n\nBoth are valid in this codebase.\n\n### 8.2 `output=` grammar\n\nCanonical form:\n\n```text\ninput1,input2,$param_a -> output1,output2\n```\n\nRules:\n\n- Left side maps function args to pipeline inputs.\n- Right side defines expected output keys from returned dict/JSON.\n- `-> None` means no output variables.\n- `$param` means value comes from server parameter file.\n\n### 8.3 Return payload expectations\n\n- For tools: return JSON-serializable dict payloads matching declared output keys.\n- For prompts: return prompt messages (typically list-like prompt payloads consumed by `get_prompt`).\n\n### 8.4 Entrypoint requirement\n\nServer modules should end with:\n\n```python\nif __name__ == \"__main__\":\n    app.run(transport=\"stdio\")\n```\n\n---\n\n## 9) Retriever/Generation/Prompt Specific Notes\n\n### Retriever (`servers/retriever`)\n\n- Supports multiple retrieval modes:\n  - Dense retrieval\n  - BM25\n  - Web search\n  - Project-memory retrieval\n- Index backends are pluggable via factory:\n  - `faiss`\n  - `milvus`\n  - `qdrant`\n- Web search backends are pluggable via factory:\n  - `exa`\n  - `tavily`\n  - `zhipuai`\n  - `serply`\n\n### Generation (`servers/generation`)\n\n- Supports generation backends including `openai`, `vllm`, and hf workflows.\n- Provides explicit cleanup tool `vllm_shutdown`.\n- Demo mode can use local streaming generation service.\n\n### Prompt (`servers/prompt`)\n\n- Uses `SandboxedEnvironment` from Jinja2 for safer rendering.\n- Validates template paths to reduce traversal risk.\n- Escapes string inputs before template rendering.\n\n---\n\n## 10) UI Backend Architecture (`ui/backend`)\n\n### `app.py`\n\n- Flask app factory (`create_app`)\n- Serves frontend static assets\n- Exposes chat/pipeline/KB/auth endpoints\n- Reads optional frontend override via `ULTRARAG_FRONTEND_DIR`\n\n### `pipeline_manager.py`\n\nThis module is large and central to UI behavior.\n\nMajor responsibilities:\n\n- Session lifecycle and streaming chat management\n- Background chat task management\n- Pipeline CRUD (list/load/save/rename/delete)\n- Parameter load/save/build wrappers\n- Knowledge base file ingest and pipeline triggering\n- Memory synchronization to per-user KB collections\n- Optional server introspection via AST stub generation if `server.yaml` is missing\n\nNotable behavior:\n\n- Applies defensive patches to suppress noisy closed-event-loop teardown logs.\n- Uses a queue bridge for async-to-sync SSE event streaming.\n- Supports automatic memory-to-KB sync for pipelines that include memory components.\n\n---\n\n## 11) Storage Model and Paths\n\nDefault UI storage root:\n\n- `ui/storage`\n\nCan be overridden by:\n\n- `ULTRARAG_UI_STORAGE_ROOT`\n\nKey subpaths:\n\n- `db/users.sqlite3`\n- `chat_sessions/`\n- `knowledge_base/raw|corpus|chunks|index`\n- `memory/`\n- `knowledge_base/_memory_sync`\n\nRuntime outputs:\n\n- `output/memory_*.json`\n- `logs/*.log`\n\n---\n\n## 12) Environment Variables You Should Know\n\n- `ULTRARAG_UI_STORAGE_ROOT`: override UI storage root\n- `ULTRARAG_FRONTEND_DIR`: override frontend static directory\n- `ULTRARAG_SESSION_TIMEOUT`: foreground chat session timeout\n- `ULTRARAG_BG_SESSION_TIMEOUT`: background session timeout\n- `ULTRARAG_LOG_TS`: custom timestamp seed for log file naming\n- `log_level`: consumed by core logger initialization\n\n---\n\n## 13) Dependency Model\n\nInstall tiers from `pyproject.toml`:\n\n- Core install: no extras\n- `retriever` extra\n- `generation` extra\n- `evaluation` extra\n- `corpus` extra\n- `all` extra (union)\n\nTypical commands:\n\n```bash\nuv sync\nuv sync --extra retriever\nuv sync --extra generation\nuv sync --all-extras\n```\n\nDevelopment dependencies include:\n\n- `ruff`\n- `ipython`\n- `jupyter`\n- `pytest`\n\n---\n\n## 14) CLI Commands (Canonical)\n\n```bash\nultrarag build <pipeline.yaml>\nultrarag run <pipeline.yaml> [--param <parameter.yaml>] [--log_level info|debug|warn|error] [--is_demo]\nultrarag show ui [--host 127.0.0.1] [--port 5050]\nultrarag show case [--config_path <memory.json>] [--host 127.0.0.1] [--port 8080]\n```\n\nMinimal smoke check:\n\n```bash\nultrarag run examples/experiments/sayhello.yaml\n```\n\n---\n\n## 15) Docker Variants\n\n- `Dockerfile`: full image (builds frontend, installs all extras)\n- `Dockerfile.base-cpu`: CPU base image\n- `Dockerfile.base-gpu`: GPU base image\n\nAll variants start UI with:\n\n```bash\nultrarag show ui --port 5050 --host 0.0.0.0\n```\n\n---\n\n## 16) Development Playbooks\n\n### 16.1 Add a new MCP server\n\n1. Create `servers/<name>/parameter.yaml`\n2. Implement `servers/<name>/src/<name>.py`\n3. Register tools/prompts via `app.tool` / `app.prompt` (or class-bound registration)\n4. Ensure `app.run(transport=\"stdio\")` exists\n5. Add server to `servers:` in a pipeline YAML\n6. Run `ultrarag build <pipeline.yaml>`\n\n### 16.2 Add a new tool to an existing server\n\n1. Implement function/method\n2. Register with explicit `output=...` contract\n3. Ensure return payload keys match outputs\n4. Update relevant parameter keys in `parameter.yaml` if using `$...`\n5. Add the step in pipeline YAML and rebuild\n\n### 16.3 Add a retriever index backend\n\n1. Implement backend in `servers/retriever/src/index_backends/`\n2. Follow `BaseIndexBackend` contract\n3. Register in `_INDEX_BACKENDS` map in `index_backends/__init__.py`\n\n### 16.4 Add a web-search backend\n\n1. Implement backend in `servers/retriever/src/websearch_backends/`\n2. Follow `BaseWebSearchBackend` contract\n3. Register in `_WEBSEARCH_BACKENDS` map\n\n### 16.5 Modify UI pipeline behavior\n\nPrimary files:\n\n- `ui/backend/app.py`\n- `ui/backend/pipeline_manager.py`\n\nIf touching build/runtime semantics, cross-check against:\n\n- `src/ultrarag/client.py`\n\n---\n\n## 17) Coding Standards (Repository-Conformant)\n\n- Use type hints for function signatures.\n- Prefer `pathlib.Path` for filesystem paths.\n- Use `yaml.safe_load` / `yaml.safe_dump`.\n- Use project logger (`get_logger` or `app.logger`) instead of `print`.\n- Keep tool outputs deterministic and JSON-serializable.\n- Keep imports grouped: stdlib -> third-party -> local.\n- Keep async boundaries explicit (`async`/`await`).\n\n---\n\n## 18) What Not To Edit Blindly\n\nTreat these as generated or runtime artifacts unless intentionally regenerating:\n\n- `servers/*/server.yaml`\n- `examples/**/parameter/*_parameter.yaml`\n- `examples/**/server/*_server.yaml`\n- `output/*`\n- `logs/*`\n- `ui/storage/*` runtime data\n\nAlso avoid committing secrets:\n\n- `.env`\n- any credential-bearing local config\n\n---\n\n## 19) Common Failure Modes and Fixes\n\n### Error: server file not found\n\n- Check `servers.<name>` path in pipeline YAML.\n- Ensure server entry script exists at `servers/<name>/src/<name>.py` (or update path in config).\n\n### Error: missing variable in pipeline execution\n\n- Verify output key names from upstream tool match downstream input names.\n- Verify remapping under step-level `output:` is correct.\n- Verify `$param` keys exist in that server's parameter config.\n\n### Remote MCP server fails to start\n\n- Ensure Node.js >= 20.\n- Confirm `npx` is available.\n- Confirm remote URL in server path is reachable.\n\n### Build succeeds but UI cannot list tools\n\n- Ensure `server.yaml` exists or can be inferred by AST parsing.\n- Check for unusual dynamic registration patterns that static analysis cannot infer.\n\n### No final answer in chat\n\n- Inspect `output/memory_*.json`.\n- Check whether generation step ran and produced `ans_ls`.\n- Check stream events in UI path (`step_start`, `step_end`, `sources`, `token`, `final`).\n\n---\n\n## 20) Validation Checklist for Agents\n\nBefore finalizing any non-trivial change:\n\n1. Build the affected pipeline:\n   - `ultrarag build <pipeline.yaml>`\n2. Run a relevant smoke case:\n   - `ultrarag run <pipeline.yaml>`\n3. If UI behavior changed:\n   - run `ultrarag show ui` and verify route behavior\n4. If retriever/generation backends changed:\n   - validate parameter schema keys and output names\n5. Keep generated artifacts intentional:\n   - do not accidentally commit transient runtime outputs\n\n---\n\n## 21) Minimal Quickstart for New Agents\n\n```bash\n# 1) Install dependencies\nuv sync --all-extras\n\n# 2) Smoke test\nultrarag run examples/experiments/sayhello.yaml\n\n# 3) Build and run a demo pipeline\nultrarag build examples/demos/LLM.yaml\nultrarag run examples/demos/LLM.yaml\n\n# 4) Launch UI\nultrarag show ui --host 127.0.0.1 --port 5050\n```\n\n---\n\n## 22) Final Notes\n\n- This repository is orchestration-first: correctness depends heavily on I/O naming consistency across tools and pipeline steps.\n- Most regressions come from mismatched variable names, stale generated configs, or incomplete parameter updates.\n- When in doubt, inspect `src/ultrarag/client.py` first: it is the execution truth.\n\n<!-- File: docs/llms.txt (Tokens: ~13212 | Category: root-instruction) -->\nThis file is a merged representation of the entire codebase, combining all repository files into a single document.\nGenerated by Repopack on: 2026-01-18T09:46:51.430Z\n\n================================================================\nFile Summary\n================================================================\n\nPurpose:\n--------\nThis file contains a packed representation of the entire repository's contents.\nIt is designed to be easily consumable by AI systems for analysis, code review,\nor other automated processes.\n\nFile Format:\n------------\nThe content is organized as follows:\n1. This summary section\n2. Repository information\n3. Repository structure\n4. Multiple file entries, each consisting of:\n  a. A separator line (================)\n  b. The file path (File: path/to/file)\n  c. Another separator line\n  d. The full contents of the file\n  e. A blank line\n\nUsage Guidelines:\n-----------------\n- This file should be treated as read-only. Any changes should be made to the\n  original repository files, not this packed version.\n- When processing this file, use the file path to distinguish\n  between different files in the repository.\n- Be aware that this file may contain sensitive information. Handle it with\n  the same level of security as you would the original repository.\n\nNotes:\n------\n- Some files may have been excluded based on .gitignore rules and Repopack's\n  configuration.\n- Binary files are not included in this packed representation. Please refer to\n  the Repository Structure section for a complete list of file paths, including\n  binary files.\n\nAdditional Info:\n----------------\n\nFor more information about Repopack, visit: https://github.com/yamadashy/repopack\n\n================================================================\nRepository Structure\n================================================================\npages/\n  en/\n    api/\n      benchmark.mdx\n      cli.mdx\n      corpus.mdx\n      custom.mdx\n      evaluation.mdx\n      generation.mdx\n      prompt.mdx\n      reranker.mdx\n      retriever.mdx\n      router.mdx\n    demo/\n      deepresearch.mdx\n      lightresearch.mdx\n      llm.mdx\n      rag.mdx\n    develop_guide/\n      case_study.mdx\n      code_integration.mdx\n      dataset.mdx\n      debug.mdx\n      parallel.mdx\n    getting_started/\n      installation.mdx\n      introduction.mdx\n      quick_start.mdx\n      update.mdx\n    pipeline/\n      light_deepresearch.mdx\n      rag.mdx\n      search_o1.mdx\n      visrag.mdx\n    rag_client/\n      branch.mdx\n      data_and_params.mdx\n      loop.mdx\n      multi_agents.mdx\n      pipeline.mdx\n      serial.mdx\n    rag_servers/\n      benchmark.mdx\n      corpus.mdx\n      custom.mdx\n      evaluation.mdx\n      generation.mdx\n      overview.mdx\n      prompt.mdx\n      reranker.mdx\n      retriever.mdx\n      router.mdx\n    ui/\n      prepare.mdx\n      start.mdx\n\n================================================================\nRepository Files\n================================================================\n\n================\nFile: pages/en/api/benchmark.mdx\n================\n---\ntitle: \"Benchmark\"\nicon: \"chart-line\"\n---\n\n## `get_data`\n\n**Signature**\n```python\n@app.tool(output=\"benchmark->q_ls,gt_ls\")\ndef get_data(benchmark: Dict[str, Any]) -> Dict[str, List[Any]]\n```\n\n**Function**\n- Multi-format Loading: Supports loading evaluation datasets in `.jsonl`, `.json`, or `.parquet` formats from local storage.\n- Dynamic Field Mapping: Uses `key_map` to map different column names in raw data (such as `question`, `answer`) to standardized output keys (usually `q_ls` and `gt_ls`).\n- Data Preprocessing: Built-in support for random shuffling (`shuffle`) and quantity truncation (`limit`).\n- Used in Demos to receive user input, treating it as a piece of data (`q_ls`).\n\n**Output Format (JSON)**\n```json\n{\n  \"q_ls\": [\"Question 1\", \"Question 2\"],\n  \"gt_ls\": [[\"Answer A1\", \"Answer A2\"], [\"Answer B\"]]\n}\n```\n\n---\n\n## Configuration\n\n```yaml servers/benchmark/parameter.yaml icon=\"/images/yaml.svg\"\nbenchmark:\n  name: nq\n  path: data/sample_nq_10.jsonl\n  key_map:\n    q_ls: question\n    gt_ls: golden_answers\n  shuffle: false\n  seed: 42\n  limit: -1\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `name` | str | Evaluation set name, used only for logging and identification (Example: `nq`) |\n| `path` | str | Data file path, supports `.jsonl`, `.json`, `.parquet` |\n| `key_map` | dict | Field mapping table, mapping raw fields to tool output keys |\n|  | `q_ls` | str| Raw field name mapped to Question List (e.g., question column in file) |\n|  | `gt_ls` | str| Raw field name mapped to Golden Answer List (e.g., golden_answers column in file) |\n| `shuffle` | bool | Whether to shuffle sample order (default `false`) |\n| `seed` | int | Random seed (effective when `shuffle=true`) |\n| `limit` | int | Upper limit of sampled data items. Default is -1 (load all), positive integer means truncation of first N items |\n\n================\nFile: pages/en/api/cli.mdx\n================\n---\ntitle: \"CLI\"\nicon: \"command\"\n---\n\n## help — Command Overview\n\n```bash\nultrarag --help\n```\n\n```text\nusage: ultrarag [-h] {build,run,show} ...\n\nUltraRAG CLI\n\npositional arguments:\n  {build,run,show}\n    build           Build the configuration\n    run             Run the pipeline with the given configuration\n    show            Show ui interfaces\n\noptions:\n  -h, --help        show this help message and exit\n```\n\n---\n\n## build — Build Configuration\n\n**Purpose**: Automatically generate a unified parameter configuration file based on `pipeline.yaml`.\n\n```bash\nultrarag build <CONFIG> [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `<CONFIG>` | Pipeline configuration file path, e.g., `examples/pipeline.yaml` |\n| `--log_level` | Log level, default `INFO` |\n\n---\n\n## run — Execute Pipeline\n\n**Purpose**: Start Pipeline execution.\n\n```bash\nultrarag run <CONFIG> [--param <PARAM_FILE>] [--is_demo] [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `<CONFIG>` | Path to `pipeline.yaml` to be executed |\n| `--param <PARAM_FILE>` | Custom parameter file path, overrides default files under `parameter/` |\n| `--is_demo` | Enable demo mode |\n| `--log_level` | Log level, default `INFO` |\n\n---\n\n## show ui — Start Web Control Interface\n\n**Purpose**: Start UI interface.\n\n```bash\nultrarag show ui [--host 127.0.0.1] [--port 5050] [--log_level DEBUG|INFO|WARN|ERROR]\n```\n\n| Parameter | Description |\n|---|---|\n| `--host` | Web service binding IP (default `127.0.0.1`) |\n| `--port` | Web service port (default `5050`) |\n| `--log_level` | Log level, default `INFO` |\n\nAccess `http://<host>:<port>` after startup to use the UI.\n\n================\nFile: pages/en/api/corpus.mdx\n================\n---\ntitle: \"Corpus\"\nicon: \"file\"\n---\n\n\n## `build_text_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,text_corpus_save_path->None\")\nasync def build_text_corpus(parse_file_path: str, text_corpus_save_path: str) -> None\n```\n\n**Function**\n- Supports .txt / .md;\n- Supports .docx (reads paragraphs and tables);\n- Supports .pdf / .xps / .oxps / .epub / .mobi / .fb2 (pure text extraction via pymupdf).\n- Recursively processes in directory mode.\n\n**Output Format (JSONL)**\n```json\n{\"id\": \"<stem>\", \"title\": \"<stem>\", \"contents\": \"<full text>\"}\n```\n---\n\n## `build_image_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,image_corpus_save_path->None\")\nasync def build_image_corpus(parse_file_path: str, image_corpus_save_path: str) -> None\n```\n\n**Function**\n- **Only supports PDF**: Renders each page as JPG (RGB) at 144DPI and validates file validity.\n- Recursively processes in directory mode.\n\n**Output Index (JSONL)**\n```json\n{\"id\": 0, \"image_id\": \"paper/page_0.jpg\", \"image_path\": \"image/paper/page_0.jpg\"}\n```\n---\n\n## `mineru_parse`\n\n**Signature**\n```python\n@app.tool(output=\"parse_file_path,mineru_dir,mineru_extra_params->None\")\nasync def mineru_parse(\n    parse_file_path: str, \n    mineru_dir: str, \n    mineru_extra_params: Optional[Dict[str, Any]] = None\n) -> None\n```\n\n**Function**\n- Calls CLI `mineru` to structurally parse PDF/directory and outputs to `mineru_dir`.\n\n---\n\n## `build_mineru_corpus`\n\n**Signature**\n```python\n@app.tool(output=\"mineru_dir,parse_file_path,text_corpus_save_path,image_corpus_save_path->None\")\nasync def build_mineru_corpus(\n    mineru_dir: str, \n    parse_file_path: str, \n    text_corpus_save_path: str, \n    image_corpus_save_path: str\n) -> None\n```\n\n**Function**\n- Aggregates MinerU parsing artifacts into **Text Corpus JSONL** and **Image Index JSONL**.\n\n\n**Output Format (JSONL)**\n- Text:\n```json\n{\"id\": \"<stem>\", \"title\": \"<stem>\", \"contents\": \"<markdown full text>\"}\n```\n- Image:\n```json\n{\"id\": 0, \"image_id\": \"paper/page_0.jpg\", \"image_path\": \"images/paper/page_0.jpg\"}\n```\n---\n\n## `chunk_documents`\n\n**Signature**\n```python\n@app.tool(output=\"raw_chunk_path,chunk_backend_configs,chunk_backend,tokenizer_or_token_counter,chunk_size,chunk_path,use_title->None\")\nasync def chunk_documents(\n    raw_chunk_path: str,\n    chunk_backend_configs: Dict[str, Any],\n    chunk_backend: str = \"token\",\n    tokenizer_or_token_counter: str = \"character\",\n    chunk_size: int = 256,\n    chunk_path: Optional[str] = None,\n    use_title: bool = True,\n) -> None\n```\n\n**Function**\n- Chunks input text corpus (JSONL, containing `id/title/contents`) into paragraphs using selected backend:\n- Chunk Backend: Supports `token` / `sentence` / `recursive`.\n- Tokenizer: `tokenizer_or_token_counter` can be `word`, `character`, or `tiktoken` encoding name (e.g., `gpt2`).\n- Chunk Size: Controls block size via `chunk_size` (overlap defaults to size/4).\n- Optionally appends document title to the beginning of each block (`use_title`).\n\n**Output Format (JSONL)**\n```json\n{\"id\": 0, \"doc_id\": \"paper\", \"title\": \"paper\", \"contents\": \"Chunked text\"}\n```\n\n---\n\n## Configuration\n\n```yaml servers/corpus/parameter.yaml icon=\"/images/yaml.svg\"\n# servers/corpus/parameter.yaml\nparse_file_path: data/UltraRAG.pdf\ntext_corpus_save_path: corpora/text.jsonl\nimage_corpus_save_path: corpora/image.jsonl\n\n# mineru\nmineru_dir: corpora/\nmineru_extra_params:\n  source: modelscope\n\n# chunking parameters\nraw_chunk_path: corpora/text.jsonl\nchunk_path: corpora/chunks.jsonl\nuse_title: false\nchunk_backend: sentence # choices=[\"token\", \"sentence\", \"recursive\"]\ntokenizer_or_token_counter: character\nchunk_size: 512\nchunk_backend_configs:\n  token:\n    chunk_overlap: 50\n  sentence:\n    chunk_overlap: 50\n    min_sentences_per_chunk: 1\n    delim: \"['.', '!', '?', '；', '。', '！', '？', '\\\\n']\"\n  recursive:\n    min_characters_per_chunk: 12\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `parse_file_path` | str | Input file or directory path |\n| `text_corpus_save_path` | str | Text corpus output path (JSONL) |\n| `image_corpus_save_path` | str | Image corpus index output path (JSONL) |\n| `mineru_dir` | str | MinerU output root directory |\n| `mineru_extra_params` | dict | MinerU extra parameters, such as `source`, `layout`, etc. |\n| `raw_chunk_path` | str | Chunking input file path (JSONL format) |\n| `chunk_path` | str | Chunking output path |\n| `use_title` | bool | Whether to append document title to the beginning of each chunk |\n| `chunk_backend` | str | Select chunking method: `token`, `sentence`, `recursive` |\n| `tokenizer_or_token_counter` | str | Tokenizer or counting method. Options: `word`, `character` or `tiktoken` model name (e.g., `gpt2`) |\n| `chunk_size` | int | Target size for each chunk |\n| `chunk_backend_configs` | dict | Configuration items for each chunking method (see below) |\n\n`chunk_backend_configs` Detailed Parameters:\n\n| Backend Type | Parameter | Description |\n|---|---|---|\n| **token** | `chunk_overlap` | Overlapping tokens between chunks |\n| **sentence** | `chunk_overlap` | Overlapping count between chunks |\n|  | `min_sentences_per_chunk` | Minimum number of sentences per chunk |\n|  | `delim` | Sentence delimiter list (Python list in string format) |\n| **recursive** | `min_characters_per_chunk` | Minimum character unit for recursive splitting |\n\n================\nFile: pages/en/api/custom.mdx\n================\n---\ntitle: \"Custom\"\nicon: \"puzzle-piece\"\n---\n\n## Search-R1 Tools\n\n### `search_r1_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef search_r1_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts query content from model response.\n- **Logic**: Uses regex `r\"<search>([^<]*)\"` to extract the content inside the last `<search>` tag. If not found, returns \"There is no query.\"; if the query does not end with `?`, it is automatically completed.\n\n### `r1_searcher_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef r1_searcher_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts query from R1-Searcher response.\n- **Logic**: Uses regex `r\"<|begin_of_query|>([^<]*)\"` to extract the last tag content.\n\n---\n\n## IRCoT & IterRetGen Tools\n\n### `iterretgen_nextquery`\n```python\n@app.tool(output=\"q_ls,ret_psg->nextq_ls\")\ndef iterretgen_nextquery(q_ls: List[str], ans_ls: List[str | Any]) -> Dict[str, List[str]]\n```\n- **Function**: Iterative retrieval generation.\n- **Logic**: `next_query = f\"{q} {ans}\"`. Concatenates original question and generated answer as the Query for the next retrieval.\n\n### `ircot_get_first_sent`\n```python\n@app.tool(output=\"ans_ls->q_ls\")\ndef ircot_get_first_sent(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts the first sentence of the answer (up to period or question/exclamation mark).\n\n### `ircot_extract_ans`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef ircot_extract_ans(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts the final answer.\n- **Logic**: Matches content after `so the answer is [...]`.\n\n---\n\n## Search-o1 Tools\n\n### `search_o1_init_list`\n```python\n@app.tool(output=\"q_ls->total_subq_list,total_reason_list,total_final_info_list\")\ndef search_o1_init_list(q_ls: List[str]) -> Dict[str, List[Any]]\n```\n- **Function**: Initializes accumulation lists required by Search-o1 (sub-questions, reasoning, final info), initially filled with `<PAD>`.\n\n### `search_o1_combine_list`\n```python\n@app.tool(output=\"total_subq_list, extract_query_list, total_reason_list, extract_reason_list->total_subq_list, total_reason_list\")\ndef search_o1_combine_list(...)\n```\n- **Function**: Appends the extracted Query and Reasoning of the current step to the total lists.\n\n### `search_o1_query_extract`\n```python\n@app.tool(output=\"ans_ls->extract_query_list\")\ndef search_o1_query_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts content between `<|begin_search_query|>...<|end_search_query|>`.\n\n### `search_o1_reasoning_extract`\n```python\n@app.tool(output=\"ans_ls->extract_reason_list\")\ndef search_o1_reasoning_extract(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts all text before `<|begin_search_query|>` as the reasoning process.\n\n### `search_o1_extract_final_information`\n```python\n@app.tool(output=\"ans_ls->extract_final_infor_list\")\ndef search_o1_extract_final_information(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts content after `**Final Information**` marker.\n\n---\n\n## Utility Tools\n\n### `output_extract_from_boxed`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef output_extract_from_boxed(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts answer from LaTeX `\\boxed{...}`. Supports nested bracket handling and format cleaning.\n\n### `merge_passages`\n```python\n@app.tool(output=\"temp_psg,ret_psg->ret_psg\")\ndef merge_passages(temp_psg: List[str | Any], ret_psg: List[str | Any]) -> Dict[str, List[str | Any]]\n```\n- **Function**: Appends `temp_psg` list to `ret_psg` list.\n\n### `evisrag_output_extract_from_special`\n```python\n@app.tool(output=\"ans_ls->pred_ls\")\ndef evisrag_output_extract_from_special(ans_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Extracts answer from `<answer>...</answer>` tags.\n\n### `assign_citation_ids` / `assign_citation_ids_stateful`\n- `assign_citation_ids`: Assigns citation IDs in the form of `[1]`, `[2]` to retrieved passages.\n- `assign_citation_ids_stateful`: Uses `CitationRegistry` class to maintain global citation IDs (cross-step deduplication).\n- `init_citation_registry`: Resets global citation registry.\n\n---\n\n## SurveyCPM Tools\n\n### `surveycpm_state_init`\n```python\n@app.tool(output=\"instruction_ls->state_ls,cursor_ls,survey_ls,step_ls,extend_time_ls,extend_result_ls,retrieved_info_ls,parsed_ls\")\ndef surveycpm_state_init(instruction_ls: List[str]) -> Dict[str, List]\n```\n- **Function**: Initializes SurveyCPM state machine.\n- **Initial State**: `state=\"search\"`, `cursor=\"outline\"`, `step=0`.\n\n### `surveycpm_parse_search_response`\n```python\n@app.tool(output=\"response_ls,surveycpm_hard_mode->keywords_ls,parsed_ls\")\ndef surveycpm_parse_search_response(response_ls: List[str], surveycpm_hard_mode: bool = True) -> Dict[str, List]\n```\n- **Function**: Parses search instructions (JSON or XML format) generated by the model, extracts keyword list.\n\n### `surveycpm_process_passages`\n```python\n@app.tool(output=\"ret_psg_ls->retrieved_info_ls\")\ndef surveycpm_process_passages(ret_psg_ls: List[List[List[str]]]) -> Dict[str, List[str]]\n```\n- **Function**: Processes retrieved passages, deduplicates, limits quantity (Top-K), and concatenates into string.\n\n### `surveycpm_after_init_plan` / `after_write` / `after_extend`\n- **Function**: Parses Agent response for different stages (initialize outline, write content, extend plan).\n- **Logic**:\n  - Calls `surveycpm_parse_response` to validate format and content.\n  - Updates `survey_ls` (outline structure) and `cursor_ls` (current cursor position) if successful.\n  - Keeps original state for retry if failed.\n\n### `surveycpm_update_state`\n```python\n@app.tool(output=\"state_ls,cursor_ls,extend_time_ls,extend_result_ls,step_ls,parsed_ls,surveycpm_max_step,surveycpm_max_extend_step->state_ls,extend_time_ls,step_ls\")\ndef surveycpm_update_state(...)\n```\n- **Function**: Core state machine logic.\n- **State Transition**:\n  - `search` -> `analyst-init_plan` (cursor=\"outline\")\n  - `search` -> `write` (cursor=section-X)\n  - `write` -> `search` (continue writing) or `analyst-extend_plan` (finished current section)\n  - `analyst-extend_plan` -> `search` (extend success) or `done` (no extension)\n  - Exceeds max steps -> `done`\n\n### `surveycpm_format_output`\n```python\n@app.tool(output=\"survey_ls,instruction_ls->ans_ls\")\ndef surveycpm_format_output(survey_ls: List[str], instruction_ls: List[str]) -> Dict[str, List[str]]\n```\n- **Function**: Converts final Survey JSON to Markdown format.\n- **Processing**: Automatically handles heading levels (# ## ###), citation formatting (`\\cite{...}` to `[1]`), and text cleaning.\n\n---\n\n## Configuration\n\n```yaml servers/custom/parameter.yaml icon=\"/images/yaml.svg\"\nsurveycpm_hard_mode: false\nsurveycpm_max_step: 140\nsurveycpm_max_extend_step: 12\n```\n\n| Parameter | Type | Description |\n|---|---|---|\n| `surveycpm_hard_mode` | bool | Whether to enable SurveyCPM's strict parsing mode (validate JSON field integrity) |\n| `surveycpm_max_step` | int | Maximum total execution steps, forced end if exceeded |\n| `surveycpm_max_extend_step` | int | Maximum plan extension times |\n\n================\nFile: pages/en/api/evaluation.mdx\n================\n---\ntitle: \"Evaluation\"\nicon: \"clipboard-check\"\n---\n\n## `evaluate`\n\n**Signature**\n```python\n@app.tool(output=\"pred_ls,gt_ls,metrics,save_path->eval_res\")\ndef evaluate(\n    pred_ls: List[str],\n    gt_ls: List[List[str]],\n    metrics: List[str] | None,\n    save_path: str,\n) -> Dict[str, Any]\n```\n\n**Function**\n- Executes automatic metric evaluation for QA/Generation tasks.\n- Supported Metrics: `acc`, `em`, `coverem`, `stringem`, `f1`, `rouge-1`, `rouge-2`, `rouge-l`.\n- Results are automatically saved as `.json` file and printed as Markdown table.\n\n\n---\n\n## `evaluate_trec`\n\n**Signature**\n```python\n@app.tool(output=\"run_path,qrels_path,ir_metrics,ks,save_path->eval_res\")\ndef evaluate_trec(\n    run_path: str,\n    qrels_path: str,\n    metrics: List[str] | None,\n    ks: List[int] | None,\n    save_path: str,\n)\n```\n\n**Function**\n- Performs IR retrieval metric evaluation based on `pytrec_eval`.\n- Reads standard TREC format:\n  - **qrels**: `<qid> <iter> <docid> <rel>`\n  - **run**: `<qid> Q0 <docid> <rank> <score> <tag>`\n- Supported Metrics: `mrr`, `map`, `recall@k`, `precision@k`, `ndcg@k`.\n- Automatically aggregates statistics and outputs as table.\n\n---\n\n## `evaluate_trec_pvalue`\n\n**Signature**\n```python\n@app.tool(\n    output=\"run_new_path,run_old_path,qrels_path,ir_metrics,ks,n_resamples,save_path->eval_res\"\n)\ndef evaluate_trec_pvalue(\n    run_new_path: str,\n    run_old_path: str,\n    qrels_path: str,\n    metrics: List[str] | None,\n    ks: List[int] | None,\n    n_resamples: int | None,\n    save_path: str,\n)\n```\n\n**Function**\n- Compares significance of two TREC result files using **Two-sided Permutation Test** to calculate p-value.\n- Default resampling count `n_resamples=10000`.\n- Outputs mean, difference, p-value, and significance flag.\n\n\n---\n\n## Configuration\n\n```yaml servers/evaluation/parameter.yaml icon=\"/images/yaml.svg\"\nsave_path: output/evaluate_results.json\n\n# QA task\nmetrics: [ 'acc', 'f1', 'em', 'coverem', 'stringem', 'rouge-1', 'rouge-2', 'rouge-l' ]\n\n# Retrieval task\nqrels_path: data/qrels.txt\nrun_path: data/run_a.txt\nks: [ 1, 5, 10, 20, 50, 100 ]\nir_metrics: [ \"mrr\", \"map\", \"recall\", \"ndcg\", \"precision\" ]\n\n# significant\nrun_new_path: data/run_a.txt\nrun_old_path: data/run_b.txt\nn_resamples: 10000\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `save_path` | str | Evaluation result save path (automatically appends timestamp) |\n| `metrics` | list[str] | Metric set used for QA / Generation tasks |\n| `qrels_path` | str | TREC format ground truth file path |\n| `run_path` | str | Result file for retrieval task |\n| `ks` | list[int] | Truncation levels for calculating NDCG@K, P@K, Recall@K, etc. |\n| `ir_metrics` | list[str] | Retrieval task metric names, supports `mrr`, `map`, `recall`, `ndcg`, `precision` |\n| `run_new_path` | str | Run file path generated by new model (significance analysis) |\n| `run_old_path` | str | Run file path of old model (significance analysis) |\n| `n_resamples` | int | Resampling count for Permutation Test |\n\n================\nFile: pages/en/api/generation.mdx\n================\n---\ntitle: \"Generation\"\nicon: \"pen-nib\"\n---\n\n## `generation_init`\n\n**Signature**\n```python\ndef generation_init(\n    backend_configs: Dict[str, Any],\n    sampling_params: Dict[str, Any],\n    extra_params: Optional[Dict[str, Any]] = None,\n    backend: str = \"vllm\",\n) -> None\n```\n\n**Function**\n- Initializes inference backend and sampling parameters.\n- Supports `vllm`, `openai`, `hf` backends.\n- `extra_params` can be used to pass `chat_template_kwargs` or other backend-specific parameters.\n\n---\n\n## `generate`\n\n**Signature**\n```python\nasync def generate(\n    prompt_ls: List[Union[str, Dict[str, Any]]],\n    system_prompt: str = \"\",\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Plain text conversation generation.\n- Automatically handles Prompt in list, supports string or OpenAI format dictionary.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"answer for prompt_0\", \"answer for prompt_1\", \"...\"]}\n```\n\n---\n\n## `multimodal_generate`\n\n**Signature**\n```python\nasync def multimodal_generate(\n    multimodal_path: List[List[str]],\n    prompt_ls: List[Union[str, Dict[str, Any]]],\n    system_prompt: str = \"\",\n    image_tag: Optional[str] = None,\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Text-image multimodal conversation generation.\n- `multimodal_path`: List of image paths corresponding to each Prompt (supports local path or URL).\n- `image_tag`: If specified (e.g., `<img>`), inserts image at that tag's position in Prompt; otherwise defaults to appending to end of Prompt.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"answer with images for prompt_0\", \"...\"]}\n```\n\n---\n\n## `multiturn_generate`\n\n**Signature**\n```python\nasync def multiturn_generate(\n    messages: List[Dict[str, str]],\n    system_prompt: str = \"\",\n) -> Dict[str, List[str]]\n```\n\n**Function**\n- Multi-turn conversation generation.\n- Supports only single-call generation, does not handle batch Prompts.\n\n**Output Format (JSON)**\n```json\n{\"ans_ls\": [\"assistant response\"]}\n```\n\n---\n\n## `vllm_shutdown`\n\n**Signature**\n```python\ndef vllm_shutdown() -> None\n```\n\n**Function**\n- Explicitly shuts down vLLM engine and releases VRAM resources.\n- Valid only when using `vllm` backend.\n\n---\n\n## Configuration\n\n```yaml servers/generation/parameter.yaml icon=\"/images/yaml.svg\"\n# servers/generation/parameter.yaml\nbackend: vllm # options: vllm, openai\nbackend_configs:\n  vllm:\n    model_name_or_path: openbmb/MiniCPM4-8B\n    gpu_ids: \"2,3\"\n    gpu_memory_utilization: 0.9\n    dtype: auto\n    trust_remote_code: true\n  openai:\n    model_name: MiniCPM4-8B\n    base_url: http://localhost:8000/v1\n    api_key: \"abc\"\n    concurrency: 8\n    retries: 3\n    base_delay: 1.0\n  hf:\n    model_name_or_path: openbmb/MiniCPM4-8B\n    gpu_ids: '2,3'\n    trust_remote_code: true\n    batch_size: 8\nsampling_params:\n  temperature: 0.7\n  top_p: 0.8\n  max_tokens: 2048\nextra_params:\n  chat_template_kwargs:\n    enable_thinking: false\nsystem_prompt: \"\"\nimage_tag: null\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `backend` | str | Specify generation backend, options `vllm`, `openai`, or `hf` (Transformers) |\n| `backend_configs` | dict | Model and runtime environment configuration for each backend |\n| `sampling_params` | dict | Sampling parameters to control generation diversity and length |\n| `extra_params` | dict | Extra parameters, e.g., `chat_template_kwargs` |\n| `system_prompt` | str | Global system prompt, added to context as `system` message |\n| `image_tag` | str | Image placeholder tag (if needed) |\n\n`backend_configs` Detailed Description:\n\n| Backend | Parameter | Description |\n|---|---|---|\n| **vllm** | `model_name_or_path` | Model name or path |\n|  | `gpu_ids` | GPU IDs used (e.g., `\"0,1\"`) |\n|  | `gpu_memory_utilization` | GPU memory utilization ratio (0–1) |\n|  | `dtype` | Data type (e.g., `auto`, `bfloat16`) |\n|  | `trust_remote_code` | Whether to trust remote code |\n| **openai** | `model_name` | OpenAI model name or self-hosted compatible model |\n|  | `base_url` | API base URL |\n|  | `api_key` | API Key |\n|  | `concurrency` | Max concurrent requests |\n|  | `retries` | API retry count |\n|  | `base_delay` | Base wait time for each retry (seconds) |\n| **hf** | `model_name_or_path` | Transformers model path |\n|  | `gpu_ids` | GPU IDs (same as above) |\n|  | `trust_remote_code` | Whether to trust remote code |\n|  | `batch_size` | Batch size per inference |\n\n`sampling_params` Detailed Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `temperature` | float | Controls randomness, higher means more diverse generation |\n| `top_p` | float | Nucleus sampling threshold |\n| `max_tokens` | int | Max generated tokens |\n\n================\nFile: pages/en/api/prompt.mdx\n================\n---\ntitle: \"Prompt\"\nicon: \"terminal\"\n---\n\n## QA Prompts\n\n### `qa_boxed`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,template->prompt_ls\")\ndef qa_boxed(\n    q_ls: List[str], \n    template: str | Path\n) -> List[PromptMessage]\n```\n\n**Function**\n\nBasic Q&A Prompt.\n\nLoads specified Jinja2 template, renders each question in the question list into a Prompt.\n\nTemplate Variable: `{{ question }}`\n\n### `qa_boxed_multiple_choice`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,choices_ls,template->prompt_ls\")\ndef qa_boxed_multiple_choice(\n    q_ls: List[str],\n    choices_ls: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nMultiple Choice Q&A Prompt.\n\nAutomatically formats choice list into \"A: ..., B: ...\" form and injects into template.\n\nTemplate Variables: `{{ question }}`, `{{ choices }}`\n\n### `qa_rag_boxed`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,template->prompt_ls\")\ndef qa_rag_boxed(\n    q_ls: List[str], \n    ret_psg: List[str | Any], \n    template: str | Path\n) -> list[PromptMessage]\n```\n\n**Function**\n\nStandard RAG Prompt.\n\nConcatenates retrieved passage lists and injects into template.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `qa_rag_boxed_multiple_choice`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,choices_ls,ret_psg,template->prompt_ls\")\ndef qa_rag_boxed_multiple_choice(\n    q_ls: List[str],\n    choices_ls: List[List[str]],\n    ret_psg: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nMultiple Choice Q&A Prompt with Retrieval Context.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`, `{{ choices }}`\n\n---\n\n## RankCoT Prompts\n\n### `RankCoT_kr`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,kr_template->prompt_ls\")\ndef RankCoT_kr(\n    q_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> list[PromptMessage]\n```\n\n**Function**\n\nRankCoT Phase 1: Knowledge Retrieval Prompt.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `RankCoT_qa`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,kr_ls,qa_template->prompt_ls\")\ndef RankCoT_qa(\n    q_ls: List[str],\n    kr_ls: List[str],\n    template: str | Path,\n) -> list[PromptMessage]\n```\n\n**Function**\n\nRankCoT Phase 2: Chain-of-Thought based Q&A Prompt.\n\nTemplate Variables: `{{ question }}`, `{{ CoT }}` (Here CoT is usually knowledge generated in the previous phase)\n\n---\n\n## IRCoT Prompts\n\n### `ircot_next_prompt`\n\n**Signature**\n\n```python\n@app.prompt(output=\"memory_q_ls,memory_ret_psg,template->prompt_ls\")\ndef ircot_next_prompt(\n    memory_q_ls: List[List[str | None]],\n    memory_ret_psg: List[List[List[str]] | None],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nIRCoT (Interleaved Retrieval CoT) Iterative Prompt Generation.\n\nConstructs next round's Prompt based on historical retrieval results and chain of thought. Supports single-turn and multi-turn history concatenation.\n\nTemplate Variables: `{{ documents }}`, `{{ question }}`, `{{ cur_answer }}`\n\n---\n\n## WebNote Prompts\n\n### `webnote_init_page`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,webnote_init_page_template->prompt_ls\")\ndef webnote_init_page(\n    q_ls: List[str],\n    plan_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Initialize note page.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`\n\n### `webnote_gen_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,webnote_gen_plan_template->prompt_ls\")\ndef webnote_gen_plan(\n    q_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate search plan.\n\nTemplate Variable: `{{ question }}`\n\n### `webnote_gen_subq`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,page_ls,webnote_gen_subq_template->prompt_ls\")\ndef webnote_gen_subq(\n    q_ls: List[str],\n    plan_ls: List[str],\n    page_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate sub-questions.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`, `{{ page }}`\n\n### `webnote_fill_page`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,plan_ls,page_ls,subq_ls,psg_ls,webnote_fill_page_template->prompt_ls\")\ndef webnote_fill_page(\n    q_ls: List[str],\n    plan_ls: List[str],\n    page_ls: List[str],\n    subq_ls: List[str],\n    psg_ls: List[Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Fill notes based on retrieval results.\n\nTemplate Variables: `{{ question }}`, `{{ plan }}`, `{{ sub_question }}`, `{{ docs_text }}`, `{{ page }}`\n\n### `webnote_gen_answer`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,page_ls,webnote_gen_answer_template->prompt_ls\")\ndef webnote_gen_answer(\n    q_ls: List[str],\n    page_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nWebNote Agent: Generate final answer based on notes.\n\nTemplate Variables: `{{ question }}`, `{{ page }}`\n\n---\n\n## Search-R1 & R1-Searcher\n\n### `search_r1_gen`\n\n**Signature**\n\n```python\n@app.prompt(output=\"prompt_ls,ans_ls,ret_psg,search_r1_gen_template->prompt_ls\")\ndef search_r1_gen(\n    prompt_ls: List[PromptMessage],\n    ans_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nGeneration Prompt suitable for R1 style.\n\nTruncates Top-3 retrieval passages and injects into context.\n\nTemplate Variables: `{{ history }}`, `{{ answer }}`, `{{ passages }}`\n\n### `r1_searcher_gen`\n\n**Signature**\n\n```python\n@app.prompt(output=\"prompt_ls,ans_ls,ret_psg,r1_searcher_gen_template->prompt_ls\")\ndef r1_searcher_gen(\n    prompt_ls: List[PromptMessage],\n    ans_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nGeneration Prompt suitable for R1 Searcher.\n\nTruncates Top-5 retrieval passages.\n\nTemplate Variables: `{{ history }}`, `{{ answer }}`, `{{ passages }}`\n\n---\n\n## Search-o1 Prompts\n\n### `search_o1_init`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,searcho1_reasoning_template->prompt_ls\")\ndef search_o1_init(\n    q_ls: List[str],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Initial Reasoning Prompt.\n\nTemplate Variable: `{{ question }}`\n\n### `search_o1_reasoning_indocument`\n\n**Signature**\n\n```python\n@app.prompt(output=\"extract_query_list,ret_psg,total_reason_list,searcho1_refine_template->prompt_ls\")\ndef search_o1_reasoning_indocument(\n    extract_query_list: List[str], \n    ret_psg: List[List[str]],       \n    total_reason_list: List[List[str]], \n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Reasoning Refinement Prompt.\n\nMerges historical reasoning steps (first + last 3 steps) with current retrieved documents for next step reasoning.\n\nTemplate Variables: `{{ prev_reasoning }}`, `{{ search_query }}`, `{{ document }}`\n\n### `search_o1_insert`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,total_subq_list,total_final_info_list,searcho1_reasoning_template->prompt_ls\") \ndef search_o1_insert(\n    q_ls: List[str],\n    total_subq_list: List[List[str]], \n    total_final_info_list: List[List[str]],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSearch-O1 Formatted Insertion Prompt.\n\nExplicitly inserts `<|begin_search_query|>` and search result tags into Prompt to construct complete chain-of-thought context.\n\n---\n\n## EVisRAG & Multi-branch Prompts\n\n### `gen_subq`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,gen_subq_template->prompt_ls\")\ndef gen_subq(\n    q_ls: List[str],\n    ret_psg: List[str | Any],\n    template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nLoop/Branch Demo: Generate sub-questions based on documents.\n\nTemplate Variables: `{{ question }}`, `{{ documents }}`\n\n### `evisrag_vqa`\n\n**Signature**\n\n```python\n@app.prompt(output=\"q_ls,ret_psg,evisrag_template->prompt_ls\")\ndef evisrag_vqa(\n    q_ls: List[str], \n    ret_psg: List[str | Any], \n    template: str | Path\n) -> list[PromptMessage]\n```\n\n**Function**\n\nMultimodal VQA RAG Prompt.\n\nAutomatically repeatedly inserts `<image>` Token into Prompt according to the number of retrieved images.\n\nTemplate Variable: `{{ question }}` (contains automatically injected image tokens)\n\n---\n\n## SurveyCPM Prompts\n\n### `surveycpm_search`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,cursor_ls,surveycpm_search_template->prompt_ls\")\ndef surveycpm_search(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    cursor_ls: List[str | None],\n    surveycpm_search_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Decide next search content.\n\nParses JSON format outline, generates text description of current outline.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_outline }}`, `{{ current_instruction }}`\n\n### `surveycpm_init_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,retrieved_info_ls,surveycpm_init_plan_template->prompt_ls\")\ndef surveycpm_init_plan(\n    instruction_ls: List[str],\n    retrieved_info_ls: List[str],\n    surveycpm_init_plan_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Initialize outline plan.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_information }}`\n\n### `surveycpm_write`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,cursor_ls,retrieved_info_ls,surveycpm_write_template->prompt_ls\")\ndef surveycpm_write(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    cursor_ls: List[str | None],\n    retrieved_info_ls: List[str],\n    surveycpm_write_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Write specific section content.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_survey }}`, `{{ current_instruction }}`, `{{ current_information }}`\n\n### `surveycpm_extend_plan`\n\n**Signature**\n\n```python\n@app.prompt(output=\"instruction_ls,survey_ls,surveycpm_extend_plan_template->prompt_ls\")\ndef surveycpm_extend_plan(\n    instruction_ls: List[str],\n    survey_ls: List[str],\n    surveycpm_extend_plan_template: str | Path,\n) -> List[PromptMessage]\n```\n\n**Function**\n\nSurvey Agent: Extend or modify outline plan.\n\nTemplate Variables: `{{ user_query }}`, `{{ current_survey }}`\n\n---\n\n## Configuration\n\n```yaml servers/prompt/parameter.yaml icon=\"/images/yaml.svg\"\n# QA\ntemplate: prompt/qa_boxed.jinja\n\n# RankCoT\nkr_template: prompt/RankCoT_knowledge_refinement.jinja\nqa_template: prompt/RankCoT_question_answering.jinja\n\n# Search-R1\nsearch_r1_gen_template: prompt/search_r1_append.jinja\n\n# R1-Searcher\nr1_searcher_gen_template: prompt/r1_searcher_append.jinja\n\n# Search-o1\nsearcho1_reasoning_template: prompt/search_o1_reasoning.jinja\nsearcho1_refine_template: prompt/search_o1_refinement.jinja\n\n\n# For other prompts, please add parameters here as needed\n\n# Take webnote as an example:\nwebnote_gen_plan_template: prompt/webnote_gen_plan.jinja\nwebnote_init_page_template: prompt/webnote_init_page.jinja\nwebnote_gen_subq_template: prompt/webnote_gen_subq.jinja\nwebnote_fill_page_template: prompt/webnote_fill_page.jinja\nwebnote_gen_answer_template: prompt/webnote_gen_answer.jinja\n\n# SurveyCPM\nsurveycpm_search_template: prompt/surveycpm_search.jinja\nsurveycpm_init_plan_template: prompt/surveycpm_init_plan.jinja\nsurveycpm_write_template: prompt/surveycpm_write.jinja\nsurveycpm_extend_plan_template: prompt/surveycpm_extend_plan.jinja\n```\n\n| Parameter | Description |\n|---|---|\n| `template` | Basic QA template path |\n| `kr_template` | RankCoT knowledge refinement template path |\n| `qa_template` | RankCoT Q&A template path |\n| `*_template` | Jinja2 template file path corresponding to each module function |\n\n================\nFile: pages/en/api/reranker.mdx\n================\n---\ntitle: \"Reranker\"\nicon: \"ranking-star\"\n---\n\n## `reranker_init`\n\n**Signature**\n```python\nasync def reranker_init(\n    model_name_or_path: str,\n    backend_configs: Dict[str, Any],\n    batch_size: int,\n    gpu_ids: Optional[object] = None,\n    backend: str = \"infinity\",\n) -> None\n```\n\n**Function**\n- Initializes reranker backend and model.\n\n---\n\n## `reranker_rerank`\n\n**Signature**\n```python\nasync def reranker_rerank(\n    query_list: List[str],\n    passages_list: List[List[str]],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n) -> Dict[str, List[Any]]\n```\n\n**Function**\n- **Reranks** candidate passages:\n\n**Output Format (JSON)**\n```json\n{\n  \"rerank_psg\": [\n    [\"best passage for q0\", \"...\"],\n    [\"best passage for q1\", \"...\"]\n  ]\n}\n```\n\n---\n\n## Configuration\n\n```yaml servers/reranker/parameter.yaml icon=\"/images/yaml.svg\"\nmodel_name_or_path: openbmb/MiniCPM-Reranker-Light\nbackend: sentence_transformers # options: infinity, sentence_transformers, openai\nbackend_configs:\n  infinity:\n    bettertransformer: false\n    pooling_method: auto\n    device: cuda\n    model_warmup: false\n    trust_remote_code: true\n  sentence_transformers:\n    device: cuda\n    trust_remote_code: true\n  openai:\n    model_name: text-embedding-3-small\n    base_url: \"https://api.openai.com/v1\"\n    api_key: \"\"\n\ngpu_ids: 0\ntop_k: 5\nbatch_size: 16\nquery_instruction: \"\"\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `model_name_or_path` | str | Model path or name (local or HuggingFace repo) |\n| `backend` | str | Select backend type: `infinity`, `sentence_transformers` or `openai` |\n| `backend_configs` | dict | Exclusive parameter settings for each backend |\n| `gpu_ids` | str/int | Specify GPU ID (can be multi-card, e.g., `\"0,1\"`) |\n| `top_k` | int | Number of reranked results returned |\n| `batch_size` | int | Sample quantity per batch |\n| `query_instruction` | str | Query prefix hint, used for prompt engineering or query modification |\n\n`backend_configs` Detailed Description:\n\n| Backend | Parameter | Description |\n|---|---|---|\n| **infinity** | `device` | Device type (cuda / cpu) |\n|  | `bettertransformer` | Whether to enable accelerated inference |\n|  | `pooling_method` | Vector pooling strategy |\n|  | `model_warmup` | Whether to warmup model |\n|  | `trust_remote_code` | Whether to trust remote code (Required for HuggingFace models) |\n| **sentence_transformers** | `device` | Device type (cuda / cpu) |\n|  | `trust_remote_code` | Whether to trust remote code |\n| **openai** | `model_name` | API Model name |\n|  | `base_url` | API access address |\n|  | `api_key` | OpenAI API Key |\n\n================\nFile: pages/en/api/retriever.mdx\n================\n---\ntitle: \"Retriever\"\nicon: \"magnifying-glass\"\n---\n\n## `retriever_init`\n\n**Signature**\n```python\nasync def retriever_init(\n    model_name_or_path: str,\n    backend_configs: Dict[str, Any],\n    batch_size: int,\n    corpus_path: str,\n    gpu_ids: Optional[object] = None,\n    is_multimodal: bool = False,\n    backend: str = \"sentence_transformers\",\n    index_backend: str = \"faiss\",\n    index_backend_configs: Optional[Dict[str, Any]] = None,\n    is_demo: bool = False,\n    collection_name: str = \"\",\n) -> None\n```\n\n**Function**\n- Initializes retrieval service.\n- Embedding Backend (backend): Responsible for converting text/images into vectors (Infinity, SentenceTransformers, OpenAI, BM25).\n- Index Backend (index_backend): Responsible for vector storage and retrieval (FAISS, Milvus, Qdrant).\n- Demo Mode: If is_demo=True, forces OpenAI + Milvus configuration, ignoring some parameters.\n\n---\n\n## `retriever_embed`\n\n**Signature**\n```python\nasync def retriever_embed(\n    embedding_path: Optional[str] = None,\n    overwrite: bool = False,\n    is_multimodal: bool = False,\n) -> None\n```\n\n**Function**\n- (Non-Demo Mode) Batches calculation of vector representations of corpus and saves as .npy file.\n- Only applies to Dense Retriever backend (BM25 not supported).\n\n\n---\n\n## `retriever_index`\n\n**Signature**\n```python\nasync def retriever_index(\n    embedding_path: str,\n    overwrite: bool = False,\n    collection_name: str = \"\",\n    corpus_path: str = \"\"\n) -> None\n```\n\n**Function**\n- Builds retrieval index.\n- FAISS: Reads embedding_path (.npy) to build local index file.\n- Milvus / Qdrant / Demo: Reads corpus_path (.jsonl), generates vectors and inserts into specified collection_name.\n\n---\n\n## `retriever_search`\n\n**Signature**\n```python\nasync def retriever_search(\n    query_list: List[str],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n    collection_name: str = \"\",\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Retrieves single or multiple queries.\n- Automatically handles query vectorization (adds query_instruction) and finds Top-K in specified collection_name (for Milvus or Qdrant) or default index.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_batch_search`\n\n**Signature**\n```python\nasync def retriever_batch_search(\n    batch_query_list: List[List[str]],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n    collection_name: str = \"\",\n) -> Dict[str, List[List[List[str]]]]\n```\n\n**Function**\n- Batch version of retriever_search, accepts nested list input.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg_ls\": [[[\"psg 1-1\"], [\"psg 1-2\"]], [[\"psg 2-1\"]]]}\n```\n\n---\n\n## `bm25_index`\n\n**Signature**\n```python\nasync def bm25_index(\n    overwrite: bool = False,\n) -> None\n```\n\n**Function**\n- When `backend=\"bm25\"`, builds BM25 sparse index and saves it.\n\n---\n\n## `bm25_search`\n\n**Signature**\n```python\nasync def bm25_search(\n    query_list: List[str],\n    top_k: int = 5,\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Keyword retrieval based on BM25 algorithm.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_deploy_search`\n\n**Signature**\n```python\nasync def retriever_deploy_search(\n    retriever_url: str,\n    query_list: List[str],\n    top_k: int = 5,\n    query_instruction: str = \"\",\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- As a client, calls remote retrieval service deployed at retriever_url for query.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"passage 1\", \"passage 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_websearch`\n\n**Signature**\n```python\nasync def retriever_websearch(\n    query_list: List[str],\n    top_k: Optional[int] | None = 5,\n    retrieve_thread_num: Optional[int] | None = 1,\n    websearch_backend: str = \"tavily\",\n    websearch_backend_configs: Optional[Dict[str, Any]] | None = None,\n) -> Dict[str, List[List[str]]]\n```\n\n**Function**\n- Unified Web retrieval tool. Choose backend via `websearch_backend` (`tavily`, `exa`, `zhipuai`) and configure in `websearch_backend_configs`.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg\": [[\"snippet 1\", \"snippet 2\"], [\"...\" ]]} \n```\n\n---\n\n## `retriever_batch_websearch`\n\n**Signature**\n```python\nasync def retriever_batch_websearch(\n    batch_query_list: List[List[str]],\n    top_k: Optional[int] | None = 5,\n    retrieve_thread_num: Optional[int] | None = 1,\n    websearch_backend: str = \"tavily\",\n    websearch_backend_configs: Optional[Dict[str, Any]] | None = None,\n) -> Dict[str, List[List[List[str]]]]\n```\n\n**Function**\n- Batch Web retrieval for SurveyCPM-style pipelines.\n\n**Output Format (JSON)**\n```json\n{\"ret_psg_ls\": [[[\"snippet 1\", \"snippet 2\"]], [\"...\" ]]} \n```\n\n\n---\n\n## Configuration\n\n```yaml servers/retriever/parameter.yaml icon=\"/images/yaml.svg\"\nmodel_name_or_path: openbmb/MiniCPM-Embedding-Light\ncorpus_path: data/corpus_example.jsonl\nembedding_path: embedding/embedding.npy\ncollection_name: wiki\n\n# Embedding Backend Configuration\nbackend: sentence_transformers # options: infinity, sentence_transformers, openai, bm25\nbackend_configs:\n  infinity:\n    bettertransformer: false\n    pooling_method: auto\n    model_warmup: false\n    trust_remote_code: true\n  sentence_transformers:\n    trust_remote_code: true\n    sentence_transformers_encode:\n      normalize_embeddings: false\n      encode_chunk_size: 256\n      q_prompt_name: query\n      psg_prompt_name: document\n      psg_task: null\n      q_task: null\n  openai:\n    model_name: text-embedding-3-small\n    base_url: \"https://api.openai.com/v1\"\n    api_key: \"abc\"\n  bm25:\n    lang: en\n    save_path: index/bm25\n\n# Index Backend Configuration\nindex_backend: faiss # options: faiss, milvus, qdrant\nindex_backend_configs:\n  faiss:\n    index_use_gpu: True\n    index_chunk_size: 10000\n    index_path: index/index.index\n  milvus:\n    uri: index/milvus_demo.db # Local file for Lite, or http://host:port\n    token: null\n    id_field_name: id\n    vector_field_name: vector\n    text_field_name: contents\n    index_params:\n      index_type: AUTOINDEX\n      metric_type: IP\n\n# Websearch Backend Configuration\nwebsearch_backend: tavily # options: tavily, exa, zhipuai\nwebsearch_backend_configs:\n  exa:\n    api_key: \"\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n  tavily:\n    api_key: \"\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n  zhipuai:\n    api_key: \"\"\n    base_url: \"https://open.bigmodel.cn/api/paas/v4/web_search\"\n    search_engine: \"search_std\"\n    search_intent: false\n    search_recency_filter: \"noLimit\"\n    content_size: \"medium\"\n    retries: 3\n    base_delay: 1.0\n    search_kwargs: {}\n\nbatch_size: 16\ntop_k: 5\ngpu_ids: \"1\"\nquery_instruction: \"\"\nis_multimodal: false\noverwrite: false\nretrieve_thread_num: 1\nretriever_url: \"http://127.0.0.1:64501\"\nis_demo: false\n```\n\nParameter Description:\n\n| Parameter | Type | Description |\n|---|---|---|\n| `model_name_or_path` | str | Retrieval model path or name (e.g., HuggingFace model ID) |\n| `corpus_path` | str | Input corpus JSONL file path |\n| `embedding_path` | str | Vector file save path (`.npy`) |\n| `collection_name` | str | Milvus or Qdrant collection name |\n| `backend` | str | Select retrieval backend: `infinity`, `sentence_transformers`, `openai`, `bm25` |\n| `index_backend` | str | Index backend: `faiss`, `milvus`, `qdrant` |\n| `backend_configs` | dict | Parameter configuration for each backend (see table below) |\n| `index_backend_configs` | dict | Parameter configuration for each index backend (see table below) |\n| `websearch_backend` | str | Web search backend: `tavily`, `exa`, `zhipuai` |\n| `websearch_backend_configs` | dict | Web search backend configs (api_key, retries, payload, etc.) |\n| `batch_size` | int | Batch size for vector generation or retrieval |\n| `top_k` | int | Number of returned candidate passages |\n| `gpu_ids` | str | Specify visible GPU devices, e.g., `\"0,1\"` |\n| `query_instruction` | str | Query prefix (used by instruction-tuning models) |\n| `is_multimodal` | bool | Whether to enable multimodal embedding (e.g., image) |\n| `overwrite` | bool | Whether to overwrite if embedding or index file already exists |\n| `retrieve_thread_num` | int | Concurrent thread number for Web retrieval |\n| `retriever_url` | str | URL of deployed retriever server |\n| `is_demo` | bool | Demo mode s\n\n<!-- File: CLAUDE.md (Tokens: ~4141 | Category: claude-rule) -->\n# AGENTS.md\n\nThis document is the primary engineering guide for autonomous coding agents working in the `UltraRAG` repository.\n\nUse this file as the source of truth for architecture, conventions, workflows, and safe change patterns.\nIf `CLAUDE.md` exists, it should only point to this file.\n\n---\n\n## 1) Project Identity\n\n`UltraRAG` is a lightweight RAG framework built around the Model Context Protocol (MCP).\nThe key design choice is strict modularization: retrieval, prompting, generation, routing, memory, and evaluation are implemented as independent MCP servers orchestrated by YAML pipelines.\n\nCurrent core metadata:\n\n- Package: `ultrarag`\n- Version: `0.3.0`\n- Python: `>=3.11, <3.13`\n- CLI entrypoint: `ultrarag = ultrarag.client:main`\n- Package manager: `uv` (`[tool.uv] package = true`)\n\n---\n\n## 2) Repository Map (What Matters Most)\n\n```text\nUltraRAG/\n├── src/ultrarag/                    # Installable core package\n│   ├── client.py                    # CLI + pipeline engine + run/build orchestration\n│   ├── server.py                    # UltraRAG_MCP_Server (FastMCP extension)\n│   ├── api.py                       # Python API wrappers (ToolCall, PipelineCall)\n│   ├── cli.py                       # Rich banner and CLI visuals\n│   ├── mcp_logging.py               # Central logging setup\n│   ├── mcp_exceptions.py            # Node.js checks for remote MCP\n│   └── utils.py                     # Subprocess lifecycle helpers\n│\n├── servers/                         # MCP microservices (each server is independent)\n│   ├── retriever/\n│   ├── generation/\n│   ├── prompt/\n│   ├── reranker/\n│   ├── benchmark/\n│   ├── evaluation/\n│   ├── corpus/\n│   ├── memory/\n│   ├── router/\n│   ├── custom/\n│   ├── pageindex/\n│   └── sayhello/\n│\n├── examples/\n│   ├── demos/                       # UI-ready demo pipelines\n│   └── experiments/                 # Experiment/research pipelines\n│\n├── ui/\n│   ├── backend/                     # Flask backend + pipeline manager\n│   └── frontend/                    # Vite + React + TypeScript\n│\n├── docs/                            # Docs and assets\n├── script/                          # Utility scripts (deploy, case study, etc.)\n├── pyproject.toml                   # Dependencies + package metadata\n├── uv.lock                          # Locked dependency graph\n├── Dockerfile*                      # Container variants\n└── .gitignore\n```\n\nImportant generated/derived files:\n\n- `servers/*/server.yaml` (generated by each server `build` tool)\n- `examples/**/parameter/*_parameter.yaml` (pipeline-merged parameters)\n- `examples/**/server/*_server.yaml` (pipeline-merged server config)\n- `output/memory_*.json` (per-run memory snapshots)\n\n---\n\n## 3) Mental Model of the System\n\nThink of UltraRAG as a three-layer system:\n\n1. **Interface layer**: CLI (`ultrarag ...`), UI (`ultrarag show ui`), and Python API (`ToolCall`, `PipelineCall`)\n2. **Orchestration layer**: `src/ultrarag/client.py` (`build`, `load_pipeline_context`, `execute_pipeline`)\n3. **Execution layer**: MCP servers in `servers/*`, each exposing tools/prompts over stdio (or remote MCP proxy)\n\nThe runtime contract is:\n\n- A pipeline YAML declares **which servers** to use and **which steps** to execute.\n- The client resolves I/O dependencies between steps.\n- Each step calls exactly one MCP tool or prompt.\n- Outputs are saved to a shared variable pool and can feed downstream steps.\n\n---\n\n## 4) Two-Phase Execution Lifecycle\n\n### Phase A: Build\n\nCommand:\n\n```bash\nultrarag build <pipeline.yaml>\n```\n\nWhat happens:\n\n- Reads `servers:` from the pipeline YAML.\n- For each referenced server, calls the server's `build` tool.\n- Produces:\n  - `<pipeline_dir>/parameter/<pipeline_name>_parameter.yaml`\n  - `<pipeline_dir>/server/<pipeline_name>_server.yaml`\n\nWhy this matters:\n\n- `build` materializes exact tool/prompt I/O metadata before runtime.\n- UI and runner rely on these generated artifacts for deterministic execution.\n\n### Phase B: Run\n\nCommand:\n\n```bash\nultrarag run <pipeline.yaml> [--param path] [--is_demo]\n```\n\nWhat happens:\n\n- Loads generated server config + parameter config.\n- Creates `fastmcp.Client` transport config for each server.\n- Executes pipeline steps in order, including `loop` and `branch`.\n- Saves intermediate memory snapshots and writes `output/memory_*.json`.\n- Invokes cleanup tools (e.g., tools ending with `vllm_shutdown`) if present.\n\n---\n\n## 5) Pipeline DSL Reference\n\nUltraRAG accepts mixed step forms:\n\n### 5.1 Plain step\n\n```yaml\n- retriever.retriever_search\n```\n\n### 5.2 Step with input/output remapping\n\n```yaml\n- generation.generate:\n    input:\n      prompt_ls: custom_prompt_ls\n    output:\n      ans_ls: final_answer_ls\n```\n\n### 5.3 Loop block\n\n```yaml\n- loop:\n    times: 3\n    steps:\n    - retriever.retriever_search\n    - generation.generate\n```\n\n### 5.4 Branch block\n\n```yaml\n- branch:\n    router:\n    - router.route_query\n    branches:\n      need_retrieval:\n      - retriever.retriever_search\n      direct_answer:\n      - generation.generate\n```\n\n### 5.5 Prompt vs Tool step semantics\n\n- Steps under the `prompt` server call `client.get_prompt(...)`.\n- Non-prompt steps call `client.call_tool(...)`.\n\n---\n\n## 6) Variable Resolution and Data Flow Rules\n\nIn `UltraData`, each tool input value is interpreted by convention:\n\n- `\"$foo\"` -> load from server-local params (`parameter.yaml`)\n- `\"bar\"`  -> read from global variable pool (`global_vars[\"bar\"]`)\n- `\"memory_xxx\"` -> read/write memory lists\n\nOutput handling:\n\n- Tool returns JSON payload -> keys mapped to declared outputs\n- Output remapping (`output:` in pipeline step) is applied at save time\n- Prompt outputs usually produce `prompt_ls`\n\nBranch handling:\n\n- Branch-aware values use wrapped list records with branch-state keys.\n- Internal sentinel (`UNSET`) is used to distinguish \"not filled yet\" from `None`.\n\nMemory handling:\n\n- The engine tracks `memory_*` histories automatically.\n- Final snapshots are serialized to `output/memory_<...>.json`.\n- If a memory server is detected, turn-level memory auto-save can be triggered.\n\n---\n\n## 7) Core Python Modules (Authoritative Guide)\n\n### `src/ultrarag/client.py`\n\nThis is the orchestration heart of the project.\n\nKey responsibilities:\n\n- CLI entrypoint (`main`)\n- UI launch (`launch_ui`) and case-study launch (`launch_case_study`)\n- Config loading (`Configuration`)\n- Pipeline data graph and state (`UltraData`)\n- Build pipeline configs (`build`)\n- Load runtime context (`load_pipeline_context`)\n- Execute step engine (`execute_pipeline`)\n- Run full pipeline (`run`)\n\nImportant runtime behaviors:\n\n- Supports both local python MCP servers (`path.endswith(\".py\")`) and remote MCP endpoints (`http(s)`).\n- For remote MCP, requires Node.js >= 20 and uses `npx -y mcp-remote <url>`.\n- Keeps loop-termination state in `ContextVar` for coroutine safety.\n- Emits structured stream events in demo/UI flows (`step_start`, `step_end`, `token`, `sources`).\n\n### `src/ultrarag/server.py`\n\nDefines `UltraRAG_MCP_Server`, a compatibility wrapper over FastMCP.\n\nKey responsibilities:\n\n- Enhanced `tool()` and `prompt()` registration with `output` metadata support\n- Metadata capture for automatic config generation\n- `build(parameter_file)` to generate per-server `server.yaml`\n- Compatibility filtering for FastMCP signature differences\n\n### `src/ultrarag/api.py`\n\nProvides ergonomic Python-side wrappers:\n\n- `initialize(servers, server_root, log_level)`\n- `ToolCall.server_name.tool_name(...)`\n- `PipelineCall(pipeline_file, parameter_file, log_level)`\n\n### `src/ultrarag/mcp_logging.py`\n\n- Initializes root logger `UltraRAG`\n- Rich console logging + file logging (`logs/<timestamp>.log`)\n- Log level controlled by `log_level` argument and environment\n\n### `src/ultrarag/mcp_exceptions.py`\n\n- Validates local Node.js availability/version\n- Raises `NodeNotInstalledError` / `NodeVersionTooLowError`\n\n### `src/ultrarag/utils.py`\n\n- Subprocess lifecycle helpers\n- POSIX parent-death signal support\n- Windows job object support for child-process cleanup\n\n---\n\n## 8) MCP Server Authoring Contract\n\nEach server follows this shape:\n\n```text\nservers/<name>/\n├── parameter.yaml\n├── server.yaml            # generated\n└── src/<name>.py\n```\n\n### 8.1 Registration styles\n\nUse either:\n\n1. Decorator style\n2. Class-bound method registration style\n\nBoth are valid in this codebase.\n\n### 8.2 `output=` grammar\n\nCanonical form:\n\n```text\ninput1,input2,$param_a -> output1,output2\n```\n\nRules:\n\n- Left side maps function args to pipeline inputs.\n- Right side defines expected output keys from returned dict/JSON.\n- `-> None` means no output variables.\n- `$param` means value comes from server parameter file.\n\n### 8.3 Return payload expectations\n\n- For tools: return JSON-serializable dict payloads matching declared output keys.\n- For prompts: return prompt messages (typically list-like prompt payloads consumed by `get_prompt`).\n\n### 8.4 Entrypoint requirement\n\nServer modules should end with:\n\n```python\nif __name__ == \"__main__\":\n    app.run(transport=\"stdio\")\n```\n\n---\n\n## 9) Retriever/Generation/Prompt Specific Notes\n\n### Retriever (`servers/retriever`)\n\n- Supports multiple retrieval modes:\n  - Dense retrieval\n  - BM25\n  - Web search\n  - Project-memory retrieval\n- Index backends are pluggable via factory:\n  - `faiss`\n  - `milvus`\n  - `qdrant`\n- Web search backends are pluggable via factory:\n  - `exa`\n  - `tavily`\n  - `zhipuai`\n  - `serply`\n\n### Generation (`servers/generation`)\n\n- Supports generation backends including `openai`, `vllm`, and hf workflows.\n- Provides explicit cleanup tool `vllm_shutdown`.\n- Demo mode can use local streaming generation service.\n\n### Prompt (`servers/prompt`)\n\n- Uses `SandboxedEnvironment` from Jinja2 for safer rendering.\n- Validates template paths to reduce traversal risk.\n- Escapes string inputs before template rendering.\n\n---\n\n## 10) UI Backend Architecture (`ui/backend`)\n\n### `app.py`\n\n- Flask app factory (`create_app`)\n- Serves frontend static assets\n- Exposes chat/pipeline/KB/auth endpoints\n- Reads optional frontend override via `ULTRARAG_FRONTEND_DIR`\n\n### `pipeline_manager.py`\n\nThis module is large and central to UI behavior.\n\nMajor responsibilities:\n\n- Session lifecycle and streaming chat management\n- Background chat task management\n- Pipeline CRUD (list/load/save/rename/delete)\n- Parameter load/save/build wrappers\n- Knowledge base file ingest and pipeline triggering\n- Memory synchronization to per-user KB collections\n- Optional server introspection via AST stub generation if `server.yaml` is missing\n\nNotable behavior:\n\n- Applies defensive patches to suppress noisy closed-event-loop teardown logs.\n- Uses a queue bridge for async-to-sync SSE event streaming.\n- Supports automatic memory-to-KB sync for pipelines that include memory components.\n\n---\n\n## 11) Storage Model and Paths\n\nDefault UI storage root:\n\n- `ui/storage`\n\nCan be overridden by:\n\n- `ULTRARAG_UI_STORAGE_ROOT`\n\nKey subpaths:\n\n- `db/users.sqlite3`\n- `chat_sessions/`\n- `knowledge_base/raw|corpus|chunks|index`\n- `memory/`\n- `knowledge_base/_memory_sync`\n\nRuntime outputs:\n\n- `output/memory_*.json`\n- `logs/*.log`\n\n---\n\n## 12) Environment Variables You Should Know\n\n- `ULTRARAG_UI_STORAGE_ROOT`: override UI storage root\n- `ULTRARAG_FRONTEND_DIR`: override frontend static directory\n- `ULTRARAG_SESSION_TIMEOUT`: foreground chat session timeout\n- `ULTRARAG_BG_SESSION_TIMEOUT`: background session timeout\n- `ULTRARAG_LOG_TS`: custom timestamp seed for log file naming\n- `log_level`: consumed by core logger initialization\n\n---\n\n## 13) Dependency Model\n\nInstall tiers from `pyproject.toml`:\n\n- Core install: no extras\n- `retriever` extra\n- `generation` extra\n- `evaluation` extra\n- `corpus` extra\n- `all` extra (union)\n\nTypical commands:\n\n```bash\nuv sync\nuv sync --extra retriever\nuv sync --extra generation\nuv sync --all-extras\n```\n\nDevelopment dependencies include:\n\n- `ruff`\n- `ipython`\n- `jupyter`\n- `pytest`\n\n---\n\n## 14) CLI Commands (Canonical)\n\n```bash\nultrarag build <pipeline.yaml>\nultrarag run <pipeline.yaml> [--param <parameter.yaml>] [--log_level info|debug|warn|error] [--is_demo]\nultrarag show ui [--host 127.0.0.1] [--port 5050]\nultrarag show case [--config_path <memory.json>] [--host 127.0.0.1] [--port 8080]\n```\n\nMinimal smoke check:\n\n```bash\nultrarag run examples/experiments/sayhello.yaml\n```\n\n---\n\n## 15) Docker Variants\n\n- `Dockerfile`: full image (builds frontend, installs all extras)\n- `Dockerfile.base-cpu`: CPU base image\n- `Dockerfile.base-gpu`: GPU base image\n\nAll variants start UI with:\n\n```bash\nultrarag show ui --port 5050 --host 0.0.0.0\n```\n\n---\n\n## 16) Development Playbooks\n\n### 16.1 Add a new MCP server\n\n1. Create `servers/<name>/parameter.yaml`\n2. Implement `servers/<name>/src/<name>.py`\n3. Register tools/prompts via `app.tool` / `app.prompt` (or class-bound registration)\n4. Ensure `app.run(transport=\"stdio\")` exists\n5. Add server to `servers:` in a pipeline YAML\n6. Run `ultrarag build <pipeline.yaml>`\n\n### 16.2 Add a new tool to an existing server\n\n1. Implement function/method\n2. Register with explicit `output=...` contract\n3. Ensure return payload keys match outputs\n4. Update relevant parameter keys in `parameter.yaml` if using `$...`\n5. Add the step in pipeline YAML and rebuild\n\n### 16.3 Add a retriever index backend\n\n1. Implement backend in `servers/retriever/src/index_backends/`\n2. Follow `BaseIndexBackend` contract\n3. Register in `_INDEX_BACKENDS` map in `index_backends/__init__.py`\n\n### 16.4 Add a web-search backend\n\n1. Implement backend in `servers/retriever/src/websearch_backends/`\n2. Follow `BaseWebSearchBackend` contract\n3. Register in `_WEBSEARCH_BACKENDS` map\n\n### 16.5 Modify UI pipeline behavior\n\nPrimary files:\n\n- `ui/backend/app.py`\n- `ui/backend/pipeline_manager.py`\n\nIf touching build/runtime semantics, cross-check against:\n\n- `src/ultrarag/client.py`\n\n---\n\n## 17) Coding Standards (Repository-Conformant)\n\n- Use type hints for function signatures.\n- Prefer `pathlib.Path` for filesystem paths.\n- Use `yaml.safe_load` / `yaml.safe_dump`.\n- Use project logger (`get_logger` or `app.logger`) instead of `print`.\n- Keep tool outputs deterministic and JSON-serializable.\n- Keep imports grouped: stdlib -> third-party -> local.\n- Keep async boundaries explicit (`async`/`await`).\n\n---\n\n## 18) What Not To Edit Blindly\n\nTreat these as generated or runtime artifacts unless intentionally regenerating:\n\n- `servers/*/server.yaml`\n- `examples/**/parameter/*_parameter.yaml`\n- `examples/**/server/*_server.yaml`\n- `output/*`\n- `logs/*`\n- `ui/storage/*` runtime data\n\nAlso avoid committing secrets:\n\n- `.env`\n- any credential-bearing local config\n\n---\n\n## 19) Common Failure Modes and Fixes\n\n### Error: server file not found\n\n- Check `servers.<name>` path in pipeline YAML.\n- Ensure server entry script exists at `servers/<name>/src/<name>.py` (or update path in config).\n\n### Error: missing variable in pipeline execution\n\n- Verify output key names from upstream tool match downstream input names.\n- Verify remapping under step-level `output:` is correct.\n- Verify `$param` keys exist in that server's parameter config.\n\n### Remote MCP server fails to start\n\n- Ensure Node.js >= 20.\n- Confirm `npx` is available.\n- Confirm remote URL in server path is reachable.\n\n### Build succeeds but UI cannot list tools\n\n- Ensure `server.yaml` exists or can be inferred by AST parsing.\n- Check for unusual dynamic registration patterns that static analysis cannot infer.\n\n### No final answer in chat\n\n- Inspect `output/memory_*.json`.\n- Check whether generation step ran and produced `ans_ls`.\n- Check stream events in UI path (`step_start`, `step_end`, `sources`, `token`, `final`).\n\n---\n\n## 20) Validation Checklist for Agents\n\nBefore finalizing any non-trivial change:\n\n1. Build the affected pipeline:\n   - `ultrarag build <pipeline.yaml>`\n2. Run a relevant smoke case:\n   - `ultrarag run <pipeline.yaml>`\n3. If UI behavior changed:\n   - run `ultrarag show ui` and verify route behavior\n4. If retriever/generation backends changed:\n   - validate parameter schema keys and output names\n5. Keep generated artifacts intentional:\n   - do not accidentally commit transient runtime outputs\n\n---\n\n## 21) Minimal Quickstart for New Agents\n\n```bash\n# 1) Install dependencies\nuv sync --all-extras\n\n# 2) Smoke test\nultrarag run examples/experiments/sayhello.yaml\n\n# 3) Build and run a demo pipeline\nultrarag build examples/demos/LLM.yaml\nultrarag run examples/demos/LLM.yaml\n\n# 4) Launch UI\nultrarag show ui --host 127.0.0.1 --port 5050\n```\n\n---\n\n## 22) Final Notes\n\n- This repository is orchestration-first: correctness depends heavily on I/O naming consistency across tools and pipeline steps.\n- Most regressions come from mismatched variable names, stale generated configs, or incomplete parameter updates.\n- When in doubt, inspect `src/ultrarag/client.py` first: it is the execution truth.\n</yakaai_skills>"}