FFFF
Skip to content

[Bug] Default delimiter varies across 11 sites; parser_config.get defaults diverge per file type, producing different chunk counts for English text #18562

Description

@skbs-eng

Self Checks

Summary

The default delimiter for parser_config.delimiter is set in at least 11 places across the codebase, with 4 distinct values. The shipped defaults diverge by callback site, and the divergence is large enough to produce a real chunk-count difference for the same input text. There is no single source of truth.

The codebase has never had a guiding question of "what should the default delimiter be?" — every new parser path has copy-pasted whichever default was nearby, and the inconsistency has accumulated silently.

Environment

  • RAGFlow: v0.27.0 (ec9c08d80, tag v0.27.0)
  • Affected files: rag/app/naive.py, deepdoc/parser/txt_parser.py, rag/app/email.py, rag/app/book.py, rag/nlp/__init__.py, web/src/components/chunk-method-dialog/use-default-parser-values.ts, web/src/pages/dataset/dataset-setting/index.tsx, web/src/components/delimiter-form-field.tsx

The 11 defaults

Grouped by category:

Group 1 — UI defaults (the value the user sees and submits)

Location Default parsed_dels
web/src/components/chunk-method-dialog/use-default-parser-values.ts:30 '\n' ['\n'] (1)
web/src/pages/dataset/dataset-setting/index.tsx:71 '\n' ['\n'] (1)
web/src/components/delimiter-form-field.tsx:78 (fallback when field.value is undefined) '\n' ['\n'] (1)

The three agree: '\n' (single newline). This is the user's "safe minimum" — no surprises.

Group 2 — Python function signature defaults (effectively dead code)

Location Default parsed_dels
rag/nlp/__init__.py:1366 naive_merge '\n。;!?' ['\n', '。', ';', '!', '?'] (5, Chinese-only)
rag/nlp/__init__.py:1429 naive_merge_with_images '\n。;!?' same
rag/nlp/__init__.py:1785 naive_merge_docx '\n。;!?' same
deepdoc/parser/txt_parser.py:30 RAGFlowTxtParser.__call__ '\n!?;。;!?' ['\n', '!', '?', ';', '。', ';', '!', '?'] (8, full set)
deepdoc/parser/txt_parser.py:35 RAGFlowTxtParser.parser_txt '\n!?;。;!?' same

These trigger only when the function is called without parser_config, which only happens in unit tests. In production (rag/svr/task_executor.py:360), chunker.chunk() is always called with parser_config=parser_config_for_chunk, so these defaults are unreachable for real users.

But they encode the maintainers' mental model of "the default delimiter" — and the two camps disagree: 3 functions say '\n。;!?', 2 say '\n!?;。;!?'.

Group 3 — parser_config.get defaults (the ones that ship)

Location Default parsed_dels
rag/app/naive.py:1137 (txt) '\n!?;。;!?' 8 delims
rag/app/naive.py:1149 (markdown) '\n!?;。;!?' 8 delims
rag/app/naive.py:1030 (docx) '\n!?。;!?' 7 delims (no ;)
rag/app/naive.py:1304 (image) '\n!?。;!?' 7 delims (no ;)
rag/app/email.py:102 '\n!?。;!?' 7 delims (no ;)
rag/app/book.py:184 '\n。;!?' 5 delims (Chinese-only)

These are the ones that actually reach users. The parser_config.get fallback is consulted when the user has set parser_config but the delimiter key is missing (e.g. they didn't fill the field). Across 5 file types, the divergence is:

  • txt, markdown — full set including ;
  • docx, image, email — same set minus ;
  • book — Chinese-only, no English punctuation at all

There is no consistent reason for this split. Both .txt and .docx typically contain English text; both contain semicolons. The ; is a hard sentence-clause separator in English prose. Removing it from .docx parsing causes chunking inconsistency: the same paragraph of text with a semicolon produces one chunk in .txt and a different (possibly oversize) chunk in .docx.

Why the split is almost certainly drift, not design

I traced the defaults through git history. The diverging values predate the recent refactors (#17383 / #17203 / #17808) — they've been in the codebase for a long time. There's no commit message or comment that explains why docx and email chose '\n!?。;!?' while txt and markdown chose '\n!?;。;!?'.

The closest thing to a designed choice is the book.py:184 default '\n。;!?' (Chinese-only). The book parser is commonly used for Chinese/foreign-language books, and a Chinese-only default makes sense for that audience. But the rest of the divergence is unexplained.

Reproduction

This is a 10-line audit script anyone can run from the repo root:

from rag.nlp.delim import parse_delimiter_field

defaults = [
    ("chunk-method-dialog/use-default-parser-values.ts:30", "\n"),
    ("dataset-setting/index.tsx:71", "\n"),
    ("delimiter-form-field.tsx:78", "\n"),
    ("naive_merge signature", "\n。;!?"),
    ("naive_merge_with_images signature", "\n。;!?"),
    ("naive_merge_docx signature", "\n。;!?"),
    ("TxtParser.__call__ signature", "\n!?;。;!?"),
    ("TxtParser.parser_txt signature", "\n!?;。;!?"),
    ("naive.py:1137 (txt)", "\n!?;。;!?"),
    ("naive.py:1149 (markdown)", "\n!?;。;!?"),
    ("naive.py:1030 (docx)", "\n!?。;!?"),
    ("naive.py:1304 (image)", "\n!?。;!?"),
    ("email.py:102", "\n!?。;!?"),
    ("book.py:184 (book)", "\n。;!?"),
]

distinct_values = sorted(set(v for _, v in defaults))
print(f"Distinct defaults in production: {len(distinct_values)}")
for v in distinct_values:
    print(f"  {v!r}  -> parsed: {parse_delimiter_field(v)}")

Expected output (on any current main):

Distinct defaults in production: 4
  '\n'                                         -> parsed: ['\n']
  '\n!?;。;!?'                              -> parsed: ['\n', '!', '?', ';', '。', ';', '!', '?']
  '\n!?。;!?'                                  -> parsed: ['\n', '!', '?', '。', ';', '!', '?']
  '\n。;!?'                                  -> parsed: ['\n', '。', ';', '!', '?']

The user-visible effect

Measured on a 2101-token sample with . after every sentence (no \n):

Default parsed_dels count Chunks for the sample Per-file-type scope
'\n' (UI) 1 1 (oversized, 2102 tokens) hypothetical fall-through
'\n!?;。;!?' (txt, markdown) 8 3 (490–1002 tokens) .txt, .md
'\n!?。;!?' (docx, image, email) 7 3 (490–1002 tokens) .docx, image, .eml
'\n。;!?' (book, naive_merge signatures) 5 1 (oversized, 2102 tokens) .book, hypothetical fall-through

For text with normal newlines the four values converge because newlines dominate the splits. For text without newlines but with punctuation — OCR'd PDFs, copy-pasted prose, normalized text — the values diverge:

  • '\n!?;。;!?' (txt, markdown) splits correctly on every sentence boundary.
  • '\n!?。;!?' (docx, image, email) splits correctly on every sentence boundary too, except at ; — so a paragraph like "The rain; the sun; the wind" becomes one oversized chunk instead of three.
  • '\n。;!?' (book) misses every English sentence boundary because it has no English punctuation. The same paragraph with English punctuation produces one giant chunk.

This is a latent bug. PR #17203 didn't touch it because the reporter's data had newlines. Users with OCR-normalized or paragraph-stripped text will hit it. The robustness fix is to consolidate to one default.

Suggested fix

A single source of truth for the shipped default. Three concrete options:

Option A — One constant, all parser_config.get calls use it

Add a constant:

# rag/nlp/delim.py
DEFAULT_DELIMITER = "\n!?;。;!?"

Replace every parser_config.get("delimiter", "...") call with parser_config.get("delimiter", DEFAULT_DELIMITER). Apply the same to the UI default in chunk-method-dialog/use-default-parser-values.ts (or leave the UI default at '\n' since that's the safe minimum).

Option B — One parser_config.get default + per-file-type English-only delisting

For parsers that don't want extra English punctuation, use the function signature default. But keep the parser_config.get at the full set for all parsers. This loses the ability to have parser-specific defaults but gains consistency.

Option C — Just fix the silent divergence

The minimum viable fix: change naive.py:1030, naive.py:1304, email.py:102 to use '\n!?;。;!?' (the same as txt/markdown). Add ; back. This alone removes the "no ; for docx/email" inconsistency in 3 lines.

The function signature defaults in rag/nlp/__init__.py can stay as-is (dead code) but ideally also collapse to one value.

Acceptance criteria

Any of these would prevent the issue:

  • One shipped default for the main parser_config.delimiter field, defined as a constant and reused across all parser_config.get calls.
  • An assert in the test suite that fails if parser_config.get("delimiter", ...) defaults diverge across parsers.
  • A note in the developer docs (or in the delim.py module docstring) explaining the intent behind the shipped default and why one value fits all parsers.

Workaround (today)

There's no per-dataset escape hatch. If a user hits the English-punctuation gap in .docx, they have to switch the parser to naive (general) or pre-process the file to substitute ; for ; (or vice versa). Not documented.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    0