Exploration Log
August 30, 2026 · View on GitHub
Chinese | English
Exploration Log
This document records the web searches, technical decisions, and validation conclusions made during the implementation of the DeepSeek-OCR1 memory plugin.
1. DeepSeek-OCR Paper
- Paper: DeepSeek-OCR: Contexts Optical Compression
- arXiv: https://arxiv.org/abs/2510.18234
- Core concept: Use DeepEncoder to compress document images into a small number of visual tokens, then decode them with DeepSeek-3B-MoE-A570M.
- Official resolution modes:
- Tiny: 512×512 → 64 tokens
- Small: 640×640 → 100 tokens
- Base: 1024×1024 → 256 tokens
- Large: 1280×1280 → 400 tokens
- Gundam: dynamic tiling
2. Papers/Implementations Related to Memory Systems
- OCR-Memory (ACL 2026): Explicitly uses DeepSeek-OCR 3B as the backbone, with SoM + Locate-and-Transcribe + age-aware multi-resolution + active recall.
- https://arxiv.org/abs/2604.26622
- No official open-source code was found.
- AgentOCR (ACL 2026 Oral): Optical self-compression + segment optical caching, with open-source code, but its backbone is Qwen2.5-VL.
- MemOCR (Meituan): A visual memory agent whose backbone is Qwen2.5-VL; it follows a similar approach rather than being a direct OCR1 implementation.
- VTC-R1: Uses optical memory for long-context reasoning; its backbone is Glyph/Qwen3-VL, not DeepSeek-OCR.
3. Local Deployment of DeepSeek-OCR
- vLLM officially supports DeepSeek-OCR, but this machine has an AMD RX 7800 XT and no NVIDIA CUDA, so the official vLLM route is not recommended.
- Community GGUF sources:
- Ollama:
ollama run deepseek-ocr(requires v0.13.0+) - Hugging Face:
ggml-org/DeepSeek-OCR-GGUF(Q8_0, recommended by the official llama.cpp blog)sabafallah/DeepSeek-OCR-GGUF(Q4_K_M / Q8_0 / BF16)NexaAI/DeepSeek-OCR-GGUF(Q4_K / Q5_K / Q6_K / Q8_0, etc.)
- Ollama:
- Empirical results:
ollama pull deepseek-ocrstalled at 84%, with speed dropping to 100KB/s, so it was abandoned.- The download succeeded using aria2c with multiple threads from
hf-mirror.com, reaching several MB/s. - The
sabafallahQ4_K_M triggered a crash in the llama.cpp CLI and may have been converted from a PR branch; however, it runs stably underllama-server. - Currently,
sabafallah'sdeepseek-ocr-Q4_K_M.gguf+mmproj-deepseek-ocr-q8_0.ggufrun stably throughllama-server.
4. llama.cpp / AMD Route
- Downloads:
llama-b10453-bin-win-vulkan-x64.zipandllama-b10453-bin-win-cpu-x64.zip - Paths:
<models>\llama.cpp\,<models>\llama.cpp-cpu\ - Runtime:
llama-serverlistens on127.0.0.1:18080- Uses
deepseek-ocr-Q4_K_M.gguf+mmproj-deepseek-ocr-q8_0.gguf(uses less VRAM)
- Key parameters:
- A
\nFree OCR.-style prompt must be used repeat_penaltyandno_repeat_ngram_sizemust be added to prevent repetitive output
- A
- Notes:
llama-mtmd-clicrashes in both Vulkan and CPU modes (0xC0000409), butllama-serverworks normally.- This was observed empirically on this machine and may be related to Windows, the driver, or the build.
5. Local Environment
- GPU: AMD Radeon RX 7800 XT (16GB) + Radeon 780M
- No NVIDIA CUDA
- Python 3.12 / Node 24 / bun 1.3
- Pillow is installed; torch is not installed
- Model files:
<models>\deepseek-ocr-gguf\
6. llama-server Runtime Notes
-c 8192is more suitable for a standalone OCR service handling 1280×1280 image tokens; when OCR and embeddings share an endpoint, the plugin defaults toocrEmbeddingContextSize=2048andocrEmbeddingUbatchSize=2048.- The plugin includes built-in automatic startup:
lib/ocr-server.js+ theautoStartOcrServerconfiguration. - Automatic startup uses a detached direct spawn of
llama-server.exe, not a PowerShell wrapper; concurrent starts for one endpoint are merged, and cancellation/failure/disposal cleans only plugin-owned processes. - Equivalent current combined CPU-service arguments:
llama-server.exe --host 127.0.0.1 --port 18080 \ -m deepseek-ocr-Q4_K_M.gguf \ --mmproj mmproj-deepseek-ocr-q8_0.gguf \ --alias deepseek-ocr -c 2048 -np 1 -n 1024 --embeddings --pooling mean -b 2048 -ub 2048
7. llama.cpp Multimodal Embedding (Real Visual Embedding)
- Conclusion: The llama.cpp
llama-server/v1/embeddingsendpoint supports image input, but the request format is not the OpenAI-standardinputstring. Instead, it is:{ "input": [ { "prompt_string": "<media_marker>", "multimodal_data": ["<raw base64>"] } ] } <media_marker>must be obtained dynamically from themedia_markerfield returned byGET /props(randomized on every startup).multimodal_datamust be raw base64, notdata:image/png;base64,...(the latter reportsFailed to load image or audio file).- Some llama.cpp builds default
-ub(physical batch size) to 512. A 1024×200 SoM memory image has approximately 784 visual tokens and reportsinput too large; the plugin's embedding/combined auto-start passes-ub 2048by default. - Empirical results:
- The current llama.cpp build supports both
/v1/chat/completionsand/v1/embeddingsunder--embeddings --pooling mean, so OCR and visual embedding can share the same service on 18080; - A marker-only request has
prompt_tokens=785; - Empty text
input:""hasprompt_tokens=1; - Direct visual tokens = 784, embedding dimension = 1280;
- The server normalizes by Euclidean norm (
--embd-normalize 2by default).
- The current llama.cpp build supports both
- Integrated into the plugin:
measureImageEmbedding/createEmbeddingHttpClient, withembedding,embeddingDim, andvisualTokensDirectpersisted invisualMemory.
8. Key Conclusions
- The plugin core can now genuinely invoke DeepSeek-OCR to read images.
- Isolated temporary testing passes; the current
npm testrun is 88/88 with the live backend (eight live-backend cases skip when it is unavailable). - Added the
ocr1_mem_metricstool: estimates the text-token/visual-token compression ratio according to the official resolution modes and recordsusage.prompt_tokensfrom real OCR requests and the approximate number of visual tokens. - Added the
ocr1_mem_updatetool: supports explicit memory updates for conflict resolution/latest-value overwrite. - Added
scripts/start-ocr-server.ps1,scripts/ensure-ocr-server.mjs, andlib/ocr-server.js. - Added the
autoStartOcrServerconfiguration to the plugin: when enabled, the plugin automatically ensures that llama-server is online when the plugin loads; an explicit port inocrBaseUrlcontrols health checks and launch, and an external already-running service is not stopped. - Comparative benchmark (
BENCHMARK.md,scripts/compare-memory.mjs): R1–R6 were fully rerun with the corrected script; dsh-ocr1-memory passed all tests; dsh-memory also passed all tests this time, but R5 had previously been marked FAIL during manual validation (it remained readable after archiving), indicating unstable behavior; R4 now has an explicit update capability. - DSH-level R1–R6 completed a full comparison in an isolated headless environment (without killing processes, running in the background): dsh-ocr1-memory passed all tests.
- Robustness enhancements have been implemented: a shared store for multiple Agents (
sharedStore), automatic recovery from missing images/corrupted caches, boundary testing for overlong input, pure-querylist, bounded tier refresh, and renderer cancellation propagation. - Maintenance is bounded, cancellable, and single-flight per namespace;
maintenanceBatchSizedefaults to 8, and plugin disposal cancels and drains automatic maintenance. - Real 1280-dimensional DeepSeek-OCR visual embeddings are now stored through marker-only requests to llama.cpp
/v1/embeddings, and the number of direct visual tokens is measured (marker-onlyprompt_tokens− empty-text baseline); the current probe is785−1=784. - Visual embedding similarity retrieval has been implemented:
measureTextEmbeddingembeds queries, andretrieveSegmentsWithEmbeddingsincorporates cosine similarity into ranking. Because cross-modal discriminability has not been sufficiently validated, the plugin disablesembeddingRetrievalby default and does not use it as an unconditional primary retrieval signal. - General agent memory testing specifications (MemoryAgentBench / LongMemEval / LoCoMo / AMB) were investigated and organized into
TEST_SPEC.md; R1–R6 map one-to-one to these specifications. - An experiment attempted to have DeepSeek-OCR directly output SoM numbers without fine-tuning: with the current llama.cpp backend, output under a custom locate prompt is unreliable (it returns irrelevant text rather than numbers), so the paper's original Locate method (model-generated numbers) cannot proceed without LoRA. The plugin's
opticalLocatorStrictpath still requires a compatible trained locator and rejects malformed output instead of falling back to text.
9. LoRA Optical Locator: Progress from Local Execution (2026-08)
9.1 Environment and Toolchain (WSL2 / RX 7800 XT / ROCm)
- Independent training venv
/root/ocr1-train-env: torch 2.11.0+rocm7.2 + torchvision 0.26.0+rocm7.2 + transformers 4.57.2 + trl 0.24 + unsloth 2026.8.22 + bitsandbytes 0.50 + datasets 5.0.1. - Key pitfalls (see the memory SOP for details):
- Under WSL, the
torch/lib/libhsa-runtime64.so*bundled with the PyTorch wheel must be deleted for the GPU to become visible (otherwiseget_device_capabilityis None); - transformers ≥5 is incompatible with DeepSeek-OCR's old remote code (strict dataclass
kv_lora_rank=None, naming differences forDeepseekV2Moe/MoE), so 4.57.x + a MoE alias patch must be used; - Unsloth
UnslothVisionDataCollatoronly supports models with animage_processor; DeepSeek-OCR does not have one → a custom collator is required.
- Under WSL, the
9.2 Training Input (Official Format, from HF Remote Code and the vLLM Processor)
images=[(patches, global_view)], where normalizedglobal_view=(1,3,1024,1024)and, when empty,patchesis(0,3,640,640); paired withimages_seq_mask(True at<image>id 128815) andimages_spatial_crop=[[1,1]].- Tokenizer single-token supervision:
0→18,1→19; only the target interval is supervised (prompt/image/space/EOS are all -100). Each HotpotQA sample has approximately 2 positive/8 negative labels. The code defaults tow+=4,w-=1for frequency balancing (the paper only disclosesw+>w-and does not give specific values). - Generation must follow the training grammar
digit space digit ... digit EOS; if only 0/1 are allowed and spaces are prohibited, the autoregressive context immediately drifts.
9.3 Empirical Results (2026-08-28)
The training set of 300 entries (seed=7) and evaluation set of 30 entries (seed=42) have zero ID overlap. Evaluation strictly uses the same grammar constraints; the table below shows the first 12 independent evaluation samples:
| Stage | Configuration | Result |
|---|---|---|
| base (without LoRA) | Same binary grammar constraints | exact 0/12, mean F1 0.139 |
| LoRA intermediate milestone | 300 entries×3 epochs, lr 1e-4, old w+=2 | exact 1/12, mean F1 0.375 |
| Single-sample overfitting | 100 steps, lr 1e-3, w+=4 | exact 1/1, F1 1.000; loss≈0 after 15 steps |
Conclusion: The complete training/vision/supervision/autoregressive decoding loop is functional; the mean F1 of the 300-entry LoRA is approximately 2.7× that of the base model. The current default code has been further corrected with target-alignment self-tests, no EOS supervision, space grammar constraints, w+=4 class balancing, and correct gradient accumulation. Reproduction of the paper's main table is not yet claimed because the paper uses a larger HotpotQA training scale and evaluations such as Mind2Web/AppWorld/RULER.
9.4 Separation of Runtime and Training
- Training: WSL ROCm, allowed to occupy the discrete GPU (this validation took from several minutes to approximately 1 hour).
- Runtime (retrieval): llama-server uses the CPU-only build (
<models>\llama.cpp-cpu\) by default and does not occupy the discrete GPU; CPU OCR/embedding has been empirically verified end to end, and isolated DSH headless validation with the official V4 Flash passed. - The following are still required for a complete reproduction at the level of the paper's main table:
- The number of pure visual tokens output layer by layer within DeepEncoder (the current llama.cpp runtime only provides interface-level statistics);
- Run the full HotpotQA training scale used in the paper and reproduce the Mind2Web/AppWorld/RULER/Mind2Web retrieval subset metrics;
- Merge/quantize the LoRA into a CPU-runnable GGUF and integrate it with
opticalLocatorEnabledfor end-to-end validation of the Locate tool in DSH.