ScholarAgent answers questions over a knowledge base by orchestrating a LangGraph tool-calling agent: Claude decides when to search, a retrieval tool pulls relevant passages (RAG), and the model answers only from what it retrieved, with inline citations. If the knowledge base doesn't contain the answer, it says so instead of hallucinating.
It demonstrates the agentic-engineering stack the way a production system would use it — agent orchestration, tool calling, retrieval-augmented generation, grounding guardrails, and (optional) LangSmith tracing.
flowchart LR
Q[Question] --> A[agent node: Claude]
A -->|tool_calls?| T[tools node: search_corpus]
T -->|retrieved passages| A
A -->|no more tool calls| ANS[Grounded, cited answer]
- agent node — Claude, bound with the
search_corpustool, decides its next move. - tools node — runs the retrieval tool and feeds results back.
- The loop repeats until Claude answers without another tool call. See
src/scholaragent/graph.py.
Grounding guardrail: the system prompt requires the agent to search first, cite
every claim as [doc_id], and refuse to answer when the corpus lacks the
information. Retrieval is TF-IDF over the local corpus (data/corpus/), so it's
fully offline and unit-testable.
make setup
make test# offline suite — no API key needed
cp .env.example .env # add your ANTHROPIC_API_KEY
make ask Q="What causes overfitting and how do you prevent it?"Example answer (grounded + cited):
Overfitting happens when a model learns the noise in the training data rather than the underlying signal, showing low training error but high validation error [overfitting]. Common remedies include more data, reducing model capacity, regularization, and early stopping [overfitting][regularization].
scholaragent/
├── data/corpus/ # the knowledge base (markdown docs)
├── src/scholaragent/
│ ├── retrieval.py # TF-IDF retriever (pure, offline)
│ ├── tools.py # search_corpus LangChain tool
│ ├── llm.py # ChatAnthropic factory
│ ├── graph.py # LangGraph tool-calling loop
│ ├── agent.py # system prompt + ask()
│ └── cli.py # command-line entry point
├── tests/ # retrieval + graph (fake-LLM) tests
├── docs/architecture.md
├── Makefile · pyproject.toml · requirements.txt
| Env var | Default | Purpose |
|---|---|---|
ANTHROPIC_API_KEY | — | Required to run the agent |
ANTHROPIC_MODEL | claude-opus-4-8 | Model (claude-haiku-4-5 for cheaper demos) |
SCHOLAR_TOP_K | 3 | Passages retrieved per search |
LANGCHAIN_TRACING_V2 | — | Set true (+ LANGCHAIN_API_KEY) for LangSmith traces |
- Swap the corpus: drop
.mdfiles intodata/corpus/. - Add tools: write a
@toolintools.pyand pass it tobuild_agent(). - Upgrade retrieval: replace the TF-IDF
Retrieverwith a vector store (e.g. FAISS or pgvector) behind the samesearch()interface.
MIT — see LICENSE.