Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Ask DeepWiki

BM25 OpenVoiceOS Plugin

This is an OVOS (OpenVoiceOS) plugin. It retrieves answers from a corpus of documents with the BM25 algorithm.

The plugin gives a lightweight baseline for several tasks: reranking, summarization, machine comprehension, and retrieval.

Install

$ pip install ovos-solver-bm25-plugin

ReRanking

Reranking refines a list of candidate answers by their relevance to a query. Use it when several candidate responses exist and you need to pick the best one.

reranker diagram

The solver ranks the candidates by their similarity to the query and selects the best match.

from ovos_bm25_solver import BM25MultipleChoiceSolver

solver = BM25MultipleChoiceSolver()
a = solver.rerank("what is the speed of light", [
    "very fast", "10m/s", "the speed of light is C"
])
print(a)
# 2024-07-22 15:03:10.295 - OVOS - __main__:load_corpus:61 - DEBUG - indexed 3 documents
# 2024-07-22 15:03:10.297 - OVOS - __main__:retrieve_from_corpus:70 - DEBUG - Rank 1 (score: 0.7198746800422668): the speed of light is C
# 2024-07-22 15:03:10.297 - OVOS - __main__:retrieve_from_corpus:70 - DEBUG - Rank 2 (score: 0.0): 10m/s
# 2024-07-22 15:03:10.297 - OVOS - __main__:retrieve_from_corpus:70 - DEBUG - Rank 3 (score: 0.0): very fast
# [(0.7198747, 'the speed of light is C'), (0.0, '10m/s'), (0.0, 'very fast')]

# NOTE: select_answer is part of the MultipleChoiceSolver base class and uses rerank internally
a = solver.select_answer("what is the speed of light", [
    "very fast", "10m/s", "the speed of light is C"
])
print(a)  # the speed of light is C

Machine Comprehension

BM25EvidenceSolverPlugin finds the sentence in a larger text that answers a user query. It scans the given passage, ranks each sentence by relevance, and returns the most informative one.

For example, when a user asks how many rovers explore Mars, the solver scans the passage below and returns the sentence that names the rovers.

evidence solver diagram

from ovos_bm25_solver import BM25EvidenceSolverPlugin

config = {
    "lang": "en-us",
    "min_conf": 0.4,
    "n_answer": 1
}
solver = BM25EvidenceSolverPlugin(config)

text = """Mars is the fourth planet from the Sun. It is a dusty, cold, desert world with a very thin atmosphere. 
Mars is also a dynamic planet with seasons, polar ice caps, canyons, extinct volcanoes, and evidence that it was even more active in the past.
Mars is one of the most explored bodies in our solar system, and it's the only planet where we've sent rovers to roam the alien landscape. 
NASA currently has two rovers (Curiosity and Perseverance), one lander (InSight), and one helicopter (Ingenuity) exploring the surface of Mars.
"""
query = "how many rovers are currently exploring Mars"
answer = solver.get_best_passage(evidence=text, question=query)
print("Query:", query)
print("Answer:", answer)
# 2024-07-22 15:05:14.209 - OVOS - __main__:load_corpus:61 - DEBUG - indexed 5 documents
# 2024-07-22 15:05:14.209 - OVOS - __main__:retrieve_from_corpus:70 - DEBUG - Rank 1 (score: 1.39238703250885): NASA currently has two rovers (Curiosity and Perseverance), one lander (InSight), and one helicopter (Ingenuity) exploring the surface of Mars.
# 2024-07-22 15:05:14.210 - OVOS - __main__:retrieve_from_corpus:70 - DEBUG - Rank 2 (score: 0.38667747378349304): Mars is one of the most explored bodies in our solar system, and it's the only planet where we've sent rovers to roam the alien landscape.
# 2024-07-22 15:05:14.210 - OVOS - __main__:retrieve_from_corpus:70 - DEBUG - Rank 3 (score: 0.15732118487358093): Mars is the fourth planet from the Sun.
# 2024-07-22 15:05:14.210 - OVOS - __main__:retrieve_from_corpus:70 - DEBUG - Rank 4 (score: 0.10177625715732574): Mars is also a dynamic planet with seasons, polar ice caps, canyons, extinct volcanoes, and evidence that it was even more active in the past.
# 2024-07-22 15:05:14.210 - OVOS - __main__:retrieve_from_corpus:70 - DEBUG - Rank 5 (score: 0.0): It is a dusty, cold, desert world with a very thin atmosphere.
# Query: how many rovers are currently exploring Mars
# Answer: NASA currently has two rovers (Curiosity and Perseverance), one lander (InSight), and one helicopter (Ingenuity) exploring the surface of Mars.

In this example, BM25EvidenceSolverPlugin finds and returns the sentence that names the number of rovers exploring Mars. Use this capability for information extraction from long text, such as a research assistant or a content summarizer.


Summarizer

BM25SummarizerPlugin performs extractive summarization. It ranks the sentences in a text and returns the most relevant ones as a concise overview.

summarizer diagram

from ovos_bm25_solver import BM25SummarizerPlugin

solver = BM25SummarizerPlugin()

# Load a large text corpus, e.g., a documentation file
with open("../ovos-technical-manual/docs/150-personas.md") as f:
    big_text = f.read()
    
# Get the summary
summary = solver.tldr(big_text, lang="en")

print(summary)
# | Component            | Role                                                         |
# |----------------------|--------------------------------------------------------------|
# | **Solver Plugin**    | Stateless text-to-text inference (e.g., Q&A, summarization). |
# | **Persona**          | Named agent composed of ordered solver plugins.              |
# | **Persona Server**   | Expose personas to other Ollama/OpenAI compatible projects.  |
# | **Persona Pipeline** | Handles persona activation and routing inside OVOS core.     |
# 
# Within `ovos-core`, the **[persona-pipeline](https://github.com/OpenVoiceOS/ovos-persona)** plugin handles all runtime logic for managing user interaction with AI agents.
# 
# ### Key Features:
# - **Composition**: Each persona consists of a name, a list of solver plugins, and optional configuration for each.
# - **Chained Execution**: When a user question is received, the persona tries solvers one by one. If the first solver fails (returns `None`), the next one is tried until a response is generated.
# - **Customizable Behavior**: Different personas can emulate different personalities or knowledge domains by varying their solver stack.

Retrieval Chatbots (Custom Knowledge Base)

Retrieval chatbots use BM25CorpusSolver to answer user queries by searching a preloaded corpus of documents or QA pairs.

Use this package to build your own solver with a dedicated corpus.

Using BM25CorpusSolver

To use BM25CorpusSolver, create an instance of the solver, load your corpus, and query it.

from ovos_bm25_solver import BM25CorpusSolver

config = {
    "lang": "en-us",
    "min_conf": 0.4,
    "n_answer": 2
}
solver = BM25CorpusSolver(config)

corpus = [
    "a cat is a feline and likes to purr",
    "a dog is the human's best friend and loves to play",
    "a bird is a beautiful animal that can fly",
    "a fish is a creature that lives in water and swims",
]
solver.load_corpus(corpus)

query = "does the fish purr like a cat?"
answer = solver.get_spoken_answer(query)
print(answer)

# Expected Output:
# 2024-07-19 20:03:29.979 - OVOS - ovos_plugin_manager.utils.config:get_plugin_config:40 - DEBUG - Loaded configuration: {'module': 'ovos-translate-plugin-server', 'lang': 'en-us'}
# 2024-07-19 20:03:30.024 - OVOS - __main__:load_corpus:28 - DEBUG - indexed 4 documents
# 2024-07-19 20:03:30.025 - OVOS - __main__:retrieve_from_corpus:37 - DEBUG - Rank 1 (score: 1.0584375858306885): a cat is a feline and likes to purr
# 2024-07-19 20:03:30.025 - OVOS - __main__:retrieve_from_corpus:37 - DEBUG - Rank 2 (score: 0.481589138507843): a fish is a creature that lives in water and swims
# a cat is a feline and likes to purr. a fish is a creature that lives in water and swims

Using BM25QACorpusSolver (Question/Answer Pairs)

BM25QACorpusSolver matches a user question to a question in the corpus and returns the matching answer.

import requests
from ovos_bm25_solver import BM25QACorpusSolver

# Load SQuAD dataset
corpus = {}
data = requests.get("https://github.com/chrischute/squad/raw/master/data/train-v2.0.json").json()
for s in data["data"]:
    for p in s["paragraphs"]:
        for qa in p["qas"]:
            if "question" in qa and qa["answers"]:
                corpus[qa["question"]] = qa["answers"][0]["text"]

# Load FreebaseQA dataset
data = requests.get("https://github.com/kelvin-jiang/FreebaseQA/raw/master/FreebaseQA-train.json").json()
for qa in data["Questions"]:
    q = qa["ProcessedQuestion"]
    a = qa["Parses"][0]["Answers"][0]["AnswersName"][0]
    corpus[q] = a

# Initialize BM25QACorpusSolver with config
config = {
    "lang": "en-us",
    "min_conf": 0.4,
    "n_answer": 1
}
solver = BM25QACorpusSolver(config)
solver.load_corpus(corpus)

query = "is there life on mars?"
answer = solver.get_spoken_answer(query)
print("Query:", query)
print("Answer:", answer)

# Expected Output:
# 86769 qa pairs imports from squad dataset
# 20357 qa pairs imports from freebaseQA dataset
# 2024-07-19 21:49:31.360 - OVOS - ovos_plugin_manager.language:create:233 - INFO - Loaded the Language Translation plugin ovos-translate-plugin-server
# 2024-07-19 21:49:31.360 - OVOS - ovos_plugin_manager.utils.config:get_plugin_config:40 - DEBUG - Loaded configuration: {'module': 'ovos-translate-plugin-server', 'lang': 'en-us'}
# 2024-07-19 21:49:32.759 - OVOS - __main__:load_corpus:61 - DEBUG - indexed 107126 documents
# Query: is there life on mars
# 2024-07-19 21:49:32.760 - OVOS - __main__:retrieve_from_corpus:70 - DEBUG - Rank 1 (score: 6.037893295288086): How is it postulated that Mars life might have evolved?
# 2024-07-19 21:49:32.760 - OVOS - __main__:retrieve_from_corpus:94 - DEBUG - closest question in corpus: How is it postulated that Mars life might have evolved?
# Answer: similar to Antarctic

In this example, BM25QACorpusSolver loads a large corpus of question-answer pairs from the SQuAD and FreebaseQA datasets, then retrieves the best matching answer for the query.

Limitations of Retrieval Chatbots

Retrieval chatbots have these limitations:

  1. Dependence on Corpus Quality and Size: Accuracy depends on the quality and coverage of the corpus. A limited or biased corpus gives inaccurate or irrelevant answers.
  2. Static Knowledge Base: Unlike generative models, retrieval chatbots cannot generate new information. They only retrieve and rephrase content already in the corpus.
  3. Contextual Understanding: BM25 ranks documents by relevance, but it can fail on nuanced or complex queries that need deep context.
  4. Scalability: As the corpus grows, indexing and retrieval need more computational resources, which can affect performance.
  5. Dynamic Updates: Keeping the corpus current is hard in fast-changing domains.

Despite these limits, retrieval chatbots work well for domains with a well-defined, mostly static corpus, such as FAQs, documentation, and knowledge bases.

Example solvers

SquadQASolver

SquadQASolver is a subclass of BM25QACorpusSolver. It loads and indexes the SQuAD dataset on initialization.

Use this solver with the ovos-persona framework.

from ovos_bm25_solver import SquadQASolver

s = SquadQASolver()
query = "is there life on mars"
print("Query:", query)
print("Answer:", s.spoken_answer(query))
# 2024-07-19 22:31:12.625 - OVOS - __main__:load_corpus:60 - DEBUG - indexed 86769 documents
# 2024-07-19 22:31:12.625 - OVOS - __main__:load_squad_corpus:119 - INFO - Loaded and indexed 86769 question-answer pairs from SQuAD dataset
# Query: is there life on mars
# 2024-07-19 22:31:12.628 - OVOS - __main__:retrieve_from_corpus:69 - DEBUG - Rank 1 (score: 6.334013938903809): How is it postulated that Mars life might have evolved?
# 2024-07-19 22:31:12.628 - OVOS - __main__:retrieve_from_corpus:93 - DEBUG - closest question in corpus: How is it postulated that Mars life might have evolved?
# Answer: similar to Antarctic

FreebaseQASolver

FreebaseQASolver is a subclass of BM25QACorpusSolver. It loads and indexes the FreebaseQA dataset on initialization.

Use this solver with the ovos-persona framework.

from ovos_bm25_solver import FreebaseQASolver

s = FreebaseQASolver()
query = "What is the capital of France"
print("Query:", query)
print("Answer:", s.spoken_answer(query))
# 2024-07-19 22:31:09.468 - OVOS - __main__:load_corpus:60 - DEBUG - indexed 20357 documents
# Query: What is the capital of France
# 2024-07-19 22:31:09.468 - OVOS - __main__:retrieve_from_corpus:69 - DEBUG - Rank 1 (score: 5.996074199676514): what is the capital of france
# 2024-07-19 22:31:09.469 - OVOS - __main__:retrieve_from_corpus:93 - DEBUG - closest question in corpus: what is the capital of france
# Answer: paris

Integrating with Persona Framework

This library is meant for use with your own corpus. You can also use SquadQASolver and FreebaseQASolver in the persona framework by defining a persona configuration file and listing the solvers to use.

Here is an example of a persona that uses SquadQASolver and FreebaseQASolver:

  1. Create a persona configuration file, e.g., qa_persona.json:
{
  "name": "QAPersona",
  "solvers": [
    "ovos-solver-squadqa-plugin",
    "ovos-solver-freebaseqa-plugin",
    "ovos-solver-failure-plugin"
  ]
}
  1. Run ovos-persona-server with the defined persona:
$ ovos-persona-server --persona qa_persona.json

In this example, the persona named "QAPersona" tries SquadQASolver first. If it finds no answer, it falls back to FreebaseQASolver. If both fail, ovos-solver-failure-plugin gives a fallback response so the persona always replies.

Check setup.py for reference on how to package your own corpus-backed solvers:

PLUGIN_ENTRY_POINTS = [
    'ovos-solver-bm25-squad-plugin=ovos_bm25_solver:SquadQASolver',
    'ovos-solver-bm25-freebase-plugin=ovos_bm25_solver:FreebaseQASolver'
]

License

This project is under the MIT license (see setup.py).


Credits

image

This work was sponsored by VisioLab, part of Royal Dutch Visio, is the test, education, and research center in the field of (innovative) assistive technology for blind and visually impaired people and professionals. We explore (new) technological developments such as Voice, VR and AI and make the knowledge and expertise we gain available to everyone.

Release files for ovos-solver-bm25-plugin 0.1.3a1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ovos-solver-bm25-plugin 0.1.3a1
File Size Uploaded
ovos_solver_bm25_plugin-0.1.3a1.tar.gz 15.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ovos-solver-bm25-plugin 0.1.3a1
File Interpreter ABI Platform
ovos_solver_bm25_plugin-0.1.3a1-py3-none-any.whl Python 3 none any Details

Total release size: 26.7 kB

Release files / ovos_solver_bm25_plugin-0.1.3a1.tar.gz

Download URL ovos_solver_bm25_plugin-0.1.3a1.tar.gz
Size 15.1 kB
Tags Source
SHA-256 checksum
How to use checksums
5d46ca1d687754f47ffcf0a76345a15d2beb2294311f1f09211fda7457c2ce6d
BLAKE2b-256 checksum
How to use checksums
0bf580b27052fcc7c1f1f0e50a34ff85aed5e9d9b651f476552087a5364e2528
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / ovos_solver_bm25_plugin-0.1.3a1-py3-none-any.whl

Download URL ovos_solver_bm25_plugin-0.1.3a1-py3-none-any.whl
Size 11.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2bd197a4048480c46af2e1ae8d22f9523d018f0723ee6806778dc8a75104fd20
BLAKE2b-256 checksum
How to use checksums
01d7a8218321068f3ce04a676f634501bd1b62063dbf04b97b76695348713c4c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page