Skip to content

[Bug]: rag/res/term.freq is loaded by term_weight but not shipped — every lowercase Latin token gets the same IDF #18414

Description

@danielgines

Summary

rag/nlp/term_weight.py loads rag/res/term.freq to build its document-frequency table. That file does not exist in the repository and is not produced by the build, so the load always fails, self.df stays empty, and the fallback assigns the same document frequency to every lowercase Latin-script token.

The result is that term weighting cannot distinguish a stop word from a content word for English, Portuguese, Spanish, French, and any other language written in the Latin alphabet. Every startup logs Load term.freq FAIL! and the warning has been silently accepted as noise.

Observed on v0.26.4.

Code

rag/nlp/term_weight.py, in __init__:

fnm = os.path.join(get_project_base_directory(), "rag/res")
self.ne, self.df = {}, {}
try:
    with open(os.path.join(fnm, "ner.json"), "r") as f:
        self.ne = json.load(f)
except Exception:
    logging.warning("Load ner.json FAIL!")
try:
    self.df = load_dict(os.path.join(fnm, "term.freq"))
except Exception:
    logging.warning("Load term.freq FAIL!")

rag/res/ in the repository contains only ner.json, synonym.json and deepdoc/. There is no term.freq, and the Dockerfile does not download one. ner.json loads fine, so this is not a packaging or permissions problem on our side — the file simply does not exist anywhere in the project.

With self.df empty, df() in weights() degenerates:

def df(t):
    if num_space_pattern.match(t):
        return 5
    if t in self.df:          # never true
        return self.df[t] + 3
    elif letter_pattern.match(t):
        return 300            # <-- every lowercase Latin token lands here
    elif len(t) >= 4:
        ...
    return 3

and idf2, which carries 70% of the weight, becomes a constant:

idf1 = np.array([idf(freq(t), 10000000) for t in tks])
idf2 = np.array([idf(df(t), 1000000000) for t in tks])
wts = (0.3 * idf1 + 0.7 * idf2) * np.array([ner(t) * postag(t) for t in tks])

freq() has the same return 300 fallback for tokens the tokenizer's trie does not know, so idf1 collapses too for these languages. ner() and postag() both return 1 for non-CJK tokens, so nothing else differentiates them.

Reproduction

from rag.nlp.term_weight import Dealer
d = Dealer()
print(len(d.df))   # 0
for t, w in sorted(d.weights(["what was the largest supplier of hospital equipment in Salvador"],
                             preprocess=True), key=lambda x: -x[1]):
    print(f"{t:<14} {w:.4f}")

Output on v0.26.4:

Salvador       0.1565
what           0.1202
was            0.1202
the            0.1202
largest        0.1202
supplier       0.1202
hospital       0.1202
equipment      0.1202

the and was carry exactly the same weight as supplier and equipment. Only Salvador differs, because its capital letter makes it miss letter_pattern. The same happens with a Portuguese sentence: every content word ties, and only one- and two-letter tokens are demoted, by the short_letter_pattern multiplier rather than by IDF.

Impact

These weights are emitted as per-term boosts in the generated full-text query (term^weight, plus bigram phrases boosted by max(w_left, w_right) * 2), so the lexical half of hybrid retrieval ranks as if every term were equally rare. It affects dataset retrieval and memory search alike.

Suggested fix

Either ship a term.freq (tab-separated term<TAB>document_frequency, which is what load_dict expects) covering at least the common function words of the supported languages, or make the fallback language-aware instead of returning a single constant for the whole Latin script. At minimum, the warning should be an error at startup, since today it announces a silently degraded ranking that reads as normal operation.

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