Future-House/paper-qa
High accuracy RAG for answering questions from scientific documents with citations
About Future-House/paper-qa
Future-House/paper-qa is an open-source project on GitHub, mainly written in Python. High accuracy RAG for answering questions from scientific documents with citations It currently holds 9,227 stars and 0 forks with 0 open issues, and was last pushed on an unknown date (repository created unknown).
Project Overview
AI Homed tracks it on the AI Models & LLM Tools board.
GitHub Repository Details
README
PaperQA2
PaperQA2 is a package for doing high-accuracy retrieval augmented generation (RAG) on PDFs, text files, Microsoft Office documents, and source code files, with a focus on the scientific literature. See our recent 2024 paper to see examples of PaperQA2's superhuman performance in scientific tasks like question answering, summarization, and contradiction detection.
---
Table of Contents
- Quickstart
- Example Output
- What is PaperQA2
- PaperQA2 vs PaperQA
- PaperQA2 Goes CalVer in December 2025
- What's New in Version 5 (aka PaperQA2)?
- What's New in December 2025?
- PaperQA2 Algorithm
- Installation
- CLI Usage
- Bundled Settings
- Rate Limits
- Library Usage
- Agentic Adding/Querying Documents
- Manual (No Agent) Adding/Querying Documents
- Async
- Choosing Model
- Locally Hosted
- Embedding Model
- Specifying the Embedding Model
- Local Embedding Models (Sentence Transformers)
- Adjusting number of sources
- Using Code or HTML
- Multimodal Support
- Using External DB/Vector DB and Caching
- Creating Index
- Manifest Files
- Reusing Index
- Using Clients Directly
- Settings Cheatsheet
- Where do I get papers?
- Callbacks
- Caching Embeddings
- Customizing Prompts
- Pre and Post Prompts
- FAQ
- How come I get different results than your papers?
- How is this different from LlamaIndex or LangChain?
- Can I save or load?
- Reproduction
- Citation
Quickstart
In this example we take a folder of research paper PDFs, magically get their metadata - including citation counts with a retraction check, then parse and cache PDFs into a full-text search index, and finally answer the user question with an LLM agent.
pip install paper-qa
mkdir my_papers
curl -o my_papers/PaperQA2.pdf https://arxiv.org/pdf/2409.13740
cd my_papers
pqa ask 'What is PaperQA2?'
Example Output
Question: Has anyone designed neural networks that compute with proteins or DNA?
The claim that neural networks have been designed to compute with DNA is supported by multiple sources.
The work by Qian, Winfree, and Bruck demonstrates the use of DNA strand displacement cascades
to construct neural network components, such as artificial neurons and associative memories,
using a DNA-based system (Qian2011Neural pages 1-2, Qian2011Neural pages 15-16, Qian2011Neural pages 54-56).
This research includes the implementation of a 3-bit XOR gate and a four-neuron Hopfield associative memory,
showcasing the potential of DNA for neural network computation.
Additionally, the application of deep learning techniques to genomics,
which involves computing with DNA sequences, is well-documented.
Studies have applied convolutional neural networks (CNNs) to predict genomic features such as
transcription factor binding and DNA accessibility (Eraslan2019Deep pages 4-5, Eraslan2019Deep pages 5-6).
These models leverage DNA sequences as input data,
effectively using neural networks to compute with DNA.
While the provided excerpts do not explicitly mention protein-based neural network computation,
they do highlight the use of neural networks in tasks related to protein sequences,
such as predicting DNA-protein binding (Zeng2016Convolutional pages 1-2).
However, the primary focus remains on DNA-based computation.
What is PaperQA2
PaperQA2 is engineered to be the best agentic RAG model for working with scientific papers. Here are some features:
- A simple interface to get good answers with grounded responses containing in-text citations.
- State-of-the-art implementation including document metadata-awareness
- Support for agentic RAG, where a language agent can iteratively refine queries and answers.
- Automatic redundant fetching of paper metadata,
- A usable full-text search engine for a local repository of PDF/text files.
- A robust interface for customization, with default support for all [LiteLLM][LiteLLM providers] models.
By default, it uses OpenAI embeddings and models with a Numpy vector DB to embed and search documents. However, you can easily use other closed-source, open-source models or embeddings (see details below).
PaperQA2 depends on some awesome libraries/APIs that make our repo possible. Here are some in no particular order:
1. Semantic Scholar 2. Crossref 3. Unpaywall 4. Pydantic 5. tantivy 6. [LiteLLM][LiteLLM general docs] 7. pybtex
PaperQA2 vs PaperQA
We've been working hard on fundamental upgrades for a while and mostly followed SemVer, until December 2025. Meaning we've incremented the major version number on each breaking change. This brings us to the current major version number v5. So why call is the repo now called PaperQA2? We wanted to remark on the fact though that we've exceeded human performance on many important metrics. So we arbitrarily call version 5 and onward PaperQA2, and versions before it as PaperQA1 to denote the significant change in performance. We recognize that we are challenged at naming and counting at FutureHouse, so we reserve the right at any time to arbitrarily change the name to PaperCrow.
PaperQA2 Goes CalVer in December 2025
Prior to December 2025 we used semantic versioning. This eventually led to confusion in two ways:
1. Developers: should we major version bump based on settings or fundamental system capabilities? What if a bug fix requires breaking changes to the agent's behaviors? 2. Speaking: should one use terminology from our publications (e.g. PaperQA1, PaperQA2) or the Git tags (e.g. v5) from this repo/package? When someone says "PaperQA" -- what version do they mean?
To resolve these confusions, in December 2025,
we moved to calendar versioning.
The developer burden is diminished because
we're basically removing guarantees of backwards compatibility across releases
(as CalVer is ZeroVer bound to dates).
It solves the "speaking" issue because Git tags are now
quite different from publication terminology (e.g. PaperQA2 vs v2025.12.17).
When someone says "PaperQA" it will just refer to the system,
not a particular snapshot of agentic behaviors.
When someone says "PaperQA2" it will refer to paper-qa>=5,
which applies to both SemVer tags v5.0.0 and the new CalVer tags v2025.12.17.
This switch is backwards compatible for version 5's SemVer, as the year 2025 is strictly greater than major version 5.
What's New in Version 5 (aka PaperQA2)?
Version 5 added:
- A CLI
pqa - Agentic workflows invoking tools for
- Removed much of the statefulness from the
Docsobject - A migration to LiteLLM for compatibility with many LLM providers
- A bundled set of configurations (read this section here))
Note that Docs objects pickled from prior versions of PaperQA are incompatible with version 5,
and will need to be rebuilt.
Also, our minimum Python version was increased to Python 3.11.
What's New in December 2025?
The last four months since version 5.29.1 have seen many changes:
- New modalities: tables, figures, non-English languages, math equations
- More and better readers
- Two new _model-based_ PDF readers: Docling
- All PDF readers now can parse images and tables, report page numbers,
- A reader for Microsoft Office data types
- Multimodal contextual summarization
- Media objects are also passed to the
summary_llmduring creation - Media objects' embedding space is enhanced using an
enrichment_llmprompt - Simpler and performant HTTP stack
- Consolidation from
aiohttpandhttpxto justhttpx - Integration with
httpx-aiohttpfor performance Contextrelevance is simplified and some assumptions were removed- Many minor features such as
Context creation upon invalid JSON,
compatibility with fall 2025's frontier LLMs,
and improved prompt templates
- Multiple fixes in metadata processing via Semantic Scholar and OpenAlex,
- Completed the deprecations accrued over the past year
PaperQA2 Algorithm
To understand PaperQA2, let's start with the pieces of the underlying algorithm. The default workflow of PaperQA2 is as follows:
| Phase | PaperQA2 Actions | | ---------------------- | ------------------------------------------------------------------------- | | 1. Paper Search | - Get candidate papers from LLM-generated keyword query | | | - Chunk, embed, and add candidate papers to state | | 2. Gather Evidence | - Embed query into vector | | | - Rank top _k_ document chunks in current state | | | - Create scored summary of each chunk in the context of the current query | | | - Use LLM to re-score and select most relevant summaries | | 3. Generate Answer | - Put best summaries into prompt with context | | | - Generate answer with prompt |
The tools can be invoked in any order by a language agent. For example, an LLM agent might do a narrow and broad search, or using different phrasing for the gather evidence step from the generate answer step.
Installation
For a non-development setup, install PaperQA2 (aka version 5) from PyPI. Note version 5 requires Python 3.11+.
pip install paper-qa>=5
For development setup, please refer to the CONTRIBUTING.md file.
PaperQA2 uses an LLM to operate,
so you'll need to either set an appropriate [API key environment variable][LiteLLM providers]
(i.e. export OPENAI_API_KEY=sk-...)
or set up an open source LLM server (i.e. using llamafile.
Any LiteLLM compatible model can be configured to use with PaperQA2.
If you need to index a large set of papers (100+),
you will likely want an API key for both
Crossref
and Semantic Scholar,
which will allow you to avoid hitting public rate limits using these metadata services.
Those can be exported as CROSSREF_API_KEY and SEMANTIC_SCHOLAR_API_KEY variables.
CLI Usage
The fastest way to test PaperQA2 is via the CLI. First navigate to a directory with some papers and use the pqa cli:
pqa ask 'What is PaperQA2?'
You will see PaperQA2 index your local PDF files, gathering the necessary metadata for each of them (using Crossref and Semantic Scholar), search over that index, then break the files into chunked evidence contexts, rank them, and ultimately generate an answer. The next time this directory is queried, your index will already be built (save for any differences detected, like new added papers), so it will skip the indexing and chunking steps.
All prior answers will be indexed and stored,
you can view them by querying via the search subcommand,
or access them yourself in your PQA_HOME directory,
which defaults to ~/.pqa/.
pqa -i 'answers' search 'ranking and contextual summarization'
PaperQA2 is highly configurable, when running from the command line,
pqa --help shows all options and short descriptions.
For example to run with a higher temperature:
pqa --temperature 0.5 ask 'What is PaperQA2?'
You can view all settings with pqa view.
Another useful thing is to change to other templated settings - for example
fast is a setting that answers more quickly
and you can see it with pqa -s fast view
Maybe you have some new settings you want to save? You can do that with
pqa -s my_new_settings --temperature 0.5 --llm foo-bar-5 save
and then you can use it with
pqa -s my_new_settings ask 'What is PaperQA2?'
If you run pqa with a command which requires a new indexing,
say if you change the default chunk_size,
a new index will automatically be created for you.
pqa --parsing.chunk_size 5000 ask 'What is PaperQA2?'
You can also use pqa to do full-text search with use of LLMs view the search command.
For example, let's save the index from a directory and give it a name:
pqa -i nanomaterials index
Now I can search for papers about thermoelectrics:
pqa -i nanomaterials search thermoelectrics
or I can use the normal ask
pqa -i nanomaterials ask 'Are there nm scale features in thermoelectric materials?'
Both the CLI and module have pre-configured settings based on prior performance and our publications, they can be invoked as follows:
pqa --settings \
ask 'Are there nm scale features in thermoelectric materials?'
Bundled Settings
Inside src/paperqa/configs we bundle known useful settings:
| Setting Name | Description |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| high_quality | Highly performant, relatively expensive (due to having evidence_k = 15) query using a ToolSelector agent. |
| fast | Setting to get answers cheaply and quickly. |
| wikicrow | Setting to emulate the Wikipedia article writing used in our WikiCrow publication. |
| contracrow | Setting to find contradictions in papers, your query should be a claim that needs to be flagged as a contradiction (or not). |
| debug | Setting useful solely for debugging, but not in any actual application beyond debugging. |
| tier1_limits | Settings that match OpenAI rate limits for each tier, you can use tier<1-5>_limits to specify the tier. |
Rate Limits
If you are hitting rate limits, say with the OpenAI Tier 1 plan, you can add them into PaperQA2. For each OpenAI tier, a pre-built setting exists to limit usage.
pqa --settings 'tier1_limits' ask 'What is PaperQA2?'
This will limit your system to use the tier1_limits, and slow down your queries to accommodate.
You can also specify them manually with any rate limit string that matches the specification in the limits module:
pqa --summary_llm_config '{"rate_limit": {"gpt-4o-2024-11-20": "30000 per 1 minute"}}' \
ask 'What is PaperQA2?'
Or by adding into a Settings object, if calling imperatively:
from paperqa import Settings, ask
answer_response = ask(
"What is PaperQA2?",
settings=Settings(
llm_config={"rate_limit": {"gpt-4o-2024-11-20": "30000 per 1 minute"}},
summary_llm_config={"rate_limit": {"gpt-4o-2024-11-20": "30000 per 1 minute"}},
),
)
Library Usage
PaperQA2's full workflow can be accessed via Python directly:
from paperqa import Settings, ask
answer_response = ask(
"What is PaperQA2?",
settings=Settings(temperature=0.5, paper_directory="my_papers"),
)
Please see our installation docs for how to install the package from PyPI.
Agentic Adding/Querying Documents
The answer object has the following attributes:
formatted_answer, answer (answer alone), question , and context (the summaries of passages found for answer).
ask will use the SearchPapers tool, which will query a local index of files,
you can specify this location via the Settings object:
from paperqa import Settings, ask
answer_response = ask(
"What is PaperQA2?",
settings=Settings(
temperature=0.5, agent={"index": {"paper_directory": "my_papers"}}
),
)
ask is just a convenience wrapper around the real entrypoint,
which can be accessed if you'd like to run concurrent asynchronous workloads:
from paperqa import Settings, agent_query
answer_response = await agent_query(
query="What is PaperQA2?",
settings=Settings(
temperature=0.5, agent={"index": {"paper_directory": "my_papers"}}
),
)
The default agent will use an LLM based agent,
but you can also specify a "fake" agent to use a hard coded call path of
search -> gather evidence -> answer to reduce token usage.
Manual (No Agent) Adding/Querying Documents
Normally via agent execution, the agent invokes the search tool,
which adds documents to the Docs object for you behind the scenes.
However, if you prefer fine-grained control,
you can directly interact with the Docs object.
Note that manually adding and querying Docs does not impact performance.
It just removes the automation associated with an agent picking the documents to add.
from paperqa import Docs, Settings
valid extensions include .pdf, .txt, .md, .html, .docx, .xlsx, .pptx, and code files (e.g., .py, .ts, .yaml)
doc_paths = ("myfile.pdf", "myotherfile.pdf")
Prepare the Docs object by adding a bunch of documents
docs = Docs()
for doc_path in doc_paths:
await docs.aadd(doc_path)
Set up how we want to query the Docs object
settings = Settings()
settings.llm = "claude-3-5-sonnet-20240620"
settings.answer.answer_max_sources = 3
Query the Docs object to get an answer
session = await docs.aquery("What is PaperQA2?", settings=settings)
print(session)
Async
PaperQA2 is written to be used asynchronously.
The synchronous API is just a wrapper around the async.
Here are the methods and their async equivalents:
| Sync | Async |
| ------------------- | -------------------- |
| Docs.add | Docs.aadd |
| Docs.add_file | Docs.aadd_file |
| Docs.add_url | Docs.aadd_url |
| Docs.get_evidence | Docs.aget_evidence |
| Docs.query | Docs.aquery |
The synchronous version just calls the async version in a loop.
Most modern python environments support async natively (including Jupyter notebooks!).
So you can do this in a Jupyter Notebook:
import asyncio
from paperqa import Docs
async def main() -> None:
docs = Docs()
# valid extensions include .pdf, .txt, .md, .html, .docx, .xlsx, .pptx, and code files (e.g., .py, .ts, .yaml)
for doc in ("myfile.pdf", "myotherfile.pdf"):
await docs.aadd(doc)
session = await docs.aquery("What is PaperQA2?")
print(session)
asyncio.run(main())
Choosing Model
By default, PaperQA2 uses OpenAI's gpt-4o-2024-11-20 model for the
summary_llm, llm, and agent_llm.
Please see the Settings Cheatsheet
for more information on these settings.
PaperQA2 also defaults to using OpenAI's text-embedding-3-small model for the embedding setting.
If you don't have an OpenAI API key, you can use a different embedding model.
More information about embedding models can be found in the "Embedding Model" section.
We use the lmi package for our LLM interface,
which in turn uses litellm to support many LLM providers.
You can adjust this easily to use any model supported by litellm:
from paperqa import Settings, ask
answer_response = ask(
"What is PaperQA2?",
settings=Settings(
llm="gpt-4o-mini", summary_llm="gpt-4o-mini", agent={"index": {"paper_directory": "my_papers"}}
),
)
To use Claude, make sure you set the ANTHROPIC_API_KEY environment variable.
In this example, we also use a different embedding model.
Please make sure to pip install paper-qa[local] to use a local embedding model.
from paperqa import Settings, ask
from paperqa.settings import AgentSettings
answer_response = ask(
"What is PaperQA2?",
settings=Settings(
llm="claude-3-5-sonnet-20240620",
summary_llm="claude-3-5-sonnet-20240620",
agent=AgentSettings(agent_llm="claude-3-5-sonnet-20240620"),
# SEE: https://huggingface.co/sentence-transformers/multi-qa-MiniLM-L6-cos-v1
embedding="st-multi-qa-MiniLM-L6-cos-v1",
),
)
Or Gemini, by setting the GEMINI_API_KEY from Google AI Studio
from paperqa import Settings, ask
from paperqa.settings import AgentSettings
answer_response = ask(
"What is PaperQA2?",
settings=Settings(
llm="gemini/gemini-2.0-flash",
summary_llm="gemini/gemini-2.0-flash",
agent=AgentSettings(agent_llm="gemini/gemini-2.0-flash"),
embedding="gemini/text-embedding-004",
),
)
Locally Hosted
You can use llama.cpp to be the LLM. Note that you should be using relatively large models, because PaperQA2 requires following