Skip to content
Building a Library Research Agent: Book Search, Citation Help, and Resource Recommendations
Learn Agentic AI15 min read13 views

Building a Library Research Agent: Book Search, Citation Help, and Resource Recommendations

Create an AI-powered library research agent that searches catalogs, formats citations in multiple styles, handles inter-library loan requests, and recommends related academic resources.

The Modern Library Challenge

Academic libraries hold vast collections across physical stacks, digital databases, and inter-library loan networks. Students often struggle to find the right resources, format citations correctly, or even know which databases to search. A library research agent transforms this experience by providing intelligent catalog search, automatic citation generation, and personalized resource recommendations.

Modeling the Library Catalog

A library catalog entry needs to represent books, journals, digital resources, and their availability.

Hear it before you finish reading

Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.

Try Live Demo →
flowchart LR
    INPUT(["User intent"])
    PARSE["Parse plus<br/>classify"]
    PLAN["Plan and tool<br/>selection"]
    AGENT["Agent loop<br/>LLM plus tools"]
    GUARD{"Guardrails<br/>and policy"}
    EXEC["Execute and<br/>verify result"]
    OBS[("Trace and metrics")]
    OUT(["Outcome plus<br/>next action"])
    INPUT --> PARSE --> PLAN --> AGENT --> GUARD
    GUARD -->|Pass| EXEC --> OUT
    GUARD -->|Fail| AGENT
    AGENT --> OBS
    style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
    style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
    style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
    style OUT fill:#059669,stroke:#047857,color:#fff
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
from datetime import date

class ResourceType(Enum):
    BOOK = "book"
    JOURNAL = "journal"
    ARTICLE = "article"
    EBOOK = "ebook"
    THESIS = "thesis"
    CONFERENCE_PAPER = "conference_paper"

class AvailabilityStatus(Enum):
    AVAILABLE = "available"
    CHECKED_OUT = "checked_out"
    ON_HOLD = "on_hold"
    DIGITAL = "digital"
    ILL_AVAILABLE = "inter_library_loan"

class CitationStyle(Enum):
    APA = "apa"
    MLA = "mla"
    CHICAGO = "chicago"
    IEEE = "ieee"

@dataclass
class LibraryResource:
    resource_id: str
    title: str
    authors: list[str]
    resource_type: ResourceType
    year: int
    isbn: Optional[str] = None
    doi: Optional[str] = None
    publisher: str = ""
    journal_name: str = ""
    volume: str = ""
    issue: str = ""
    pages: str = ""
    subjects: list[str] = field(default_factory=list)
    availability: AvailabilityStatus = AvailabilityStatus.AVAILABLE
    location: str = ""
    call_number: str = ""
    abstract: str = ""

Citation Formatter

One of the most common library requests is help formatting citations. The agent needs a reliable formatter.

def format_citation(
    resource: LibraryResource, style: CitationStyle
) -> str:
    authors_str = _format_authors(resource.authors, style)

    if style == CitationStyle.APA:
        if resource.resource_type == ResourceType.BOOK:
            return (
                f"{authors_str} ({resource.year}). "
                f"*{resource.title}*. {resource.publisher}."
            )
        elif resource.resource_type in (
            ResourceType.ARTICLE, ResourceType.JOURNAL
        ):
            return (
                f"{authors_str} ({resource.year}). "
                f"{resource.title}. *{resource.journal_name}*, "
                f"*{resource.volume}*({resource.issue}), "
                f"{resource.pages}."
            )

    elif style == CitationStyle.MLA:
        if resource.resource_type == ResourceType.BOOK:
            return (
                f"{authors_str}. *{resource.title}*. "
                f"{resource.publisher}, {resource.year}."
            )

    elif style == CitationStyle.IEEE:
        if resource.resource_type == ResourceType.ARTICLE:
            return (
                f"{authors_str}, \"{resource.title},\" "
                f"*{resource.journal_name}*, vol. {resource.volume}, "
                f"no. {resource.issue}, pp. {resource.pages}, "
                f"{resource.year}."
            )

    return f"{authors_str}. {resource.title}. {resource.year}."

def _format_authors(
    authors: list[str], style: CitationStyle
) -> str:
    if not authors:
        return "Unknown"
    if style == CitationStyle.APA:
        if len(authors) == 1:
            parts = authors[0].split()
            return f"{parts[-1]}, {parts[0][0]}."
        formatted = []
        for author in authors[:6]:
            parts = author.split()
            formatted.append(f"{parts[-1]}, {parts[0][0]}.")
        if len(authors) > 6:
            return ", ".join(formatted) + ", ... et al."
        return ", ".join(formatted[:-1]) + ", & " + formatted[-1]
    return " and ".join(authors)

Agent Tools for Library Search and Recommendations

from agents import Agent, function_tool, Runner
import json

CATALOG: dict[str, LibraryResource] = {}

@function_tool
def search_catalog(
    query: str,
    resource_type: str = "",
    subject: str = "",
) -> str:
    """Search the library catalog by keyword, type, and subject."""
    results = []
    query_lower = query.lower()
    for res in CATALOG.values():
        title_match = query_lower in res.title.lower()
        author_match = any(
            query_lower in a.lower() for a in res.authors
        )
        subject_match = any(
            query_lower in s.lower() for s in res.subjects
        )
        if not (title_match or author_match or subject_match):
            continue
        if resource_type and res.resource_type.value != resource_type:
            continue
        if subject and not any(
            subject.lower() in s.lower() for s in res.subjects
        ):
            continue
        results.append({
            "id": res.resource_id,
            "title": res.title,
            "authors": res.authors,
            "year": res.year,
            "type": res.resource_type.value,
            "availability": res.availability.value,
            "location": res.location,
            "call_number": res.call_number,
        })
    return json.dumps(results[:10]) if results else "No results found."

@function_tool
def generate_citation(
    resource_id: str, style: str = "apa"
) -> str:
    """Generate a formatted citation for a resource."""
    resource = CATALOG.get(resource_id)
    if not resource:
        return "Resource not found."
    try:
        citation_style = CitationStyle(style.lower())
    except ValueError:
        return f"Unsupported style. Use: apa, mla, chicago, ieee"
    return format_citation(resource, citation_style)

@function_tool
def find_related_resources(resource_id: str) -> str:
    """Find resources related to a given resource by shared subjects."""
    source = CATALOG.get(resource_id)
    if not source:
        return "Resource not found."
    source_subjects = set(s.lower() for s in source.subjects)
    related = []
    for rid, res in CATALOG.items():
        if rid == resource_id:
            continue
        res_subjects = set(s.lower() for s in res.subjects)
        overlap = source_subjects & res_subjects
        if overlap:
            related.append({
                "id": rid,
                "title": res.title,
                "authors": res.authors,
                "shared_subjects": list(overlap),
                "relevance_score": len(overlap) / len(source_subjects),
            })
    related.sort(key=lambda r: r["relevance_score"], reverse=True)
    return json.dumps(related[:5])

library_agent = Agent(
    name="Library Research Assistant",
    instructions="""You are an academic library research assistant.
    Help patrons search the catalog, generate properly formatted
    citations, find related resources, and request inter-library
    loans. When a resource is checked out, suggest alternatives
    or offer to place a hold. Always ask which citation style the
    patron needs before generating citations.""",
    tools=[search_catalog, generate_citation, find_related_resources],
)

FAQ

How does the agent handle resources from external databases like JSTOR or PubMed?

Implement additional tool functions that call external APIs. JSTOR and PubMed provide REST APIs that return structured metadata. The agent can search these alongside the local catalog and clearly indicate which resources are available locally versus externally.

Can the agent detect plagiarism or verify citation accuracy?

The agent can verify that a citation matches the source metadata (correct authors, year, title) by comparing against catalog records. For plagiarism detection, integrate with services like Turnitin via their API. The agent should frame this as a verification service, not an accusation tool.

Still reading? Stop comparing — try CallSphere live.

CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.

How do you handle multi-branch library systems?

Add a branch field to each resource and a preferred_branch to the patron record. The search tool returns availability per branch, and the agent can suggest the nearest location with an available copy or offer to initiate a transfer between branches.


#AIAgents #EdTech #LibraryScience #Python #Research #AgenticAI #LearnAI #AIEngineering

Share

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.

Related Articles You May Like

AI Agents

Personal AI Assistant: How to Pick One for Business in 2026

A founder's guide to the personal AI assistant market: best AI assistant apps, business-grade options, and how CallSphere's voice agent fits in.

AI Agents

Free AI Agents in 2026: When Free Wins and When It Costs You

A founder's guide to free AI agents, low-code AI agent builders, and how to know when you should pay for a real platform like CallSphere.

Agentic AI

Graphiti: How Temporal Knowledge Graphs Give AI Voice Agents Persistent Memory (2026 Guide)

Graphiti is the open-source temporal knowledge graph for AI agents in 2026. Learn how bi-temporal memory beats vector RAG for voice agents and long-running LLMs.

AI Agents

Chatbot App vs ChatGPT: What's the Difference, and Which Do I Need?

Chatbot app vs ChatGPT in 2026: a founder's clear take on the difference, when to use which, and how a real AI chatbot app development works.

HVAC

Building an HVAC After-Hours Emergency Escalation System: A Complete Engineering Guide

How we built a fault-tolerant HVAC emergency triage and tech-dispatch platform on Kubernetes — three-tier CQRS, 11 micro-agents on the OpenAI Agents SDK + LangGraph, NATS JetStream, DTMF/SMS/WebSocket acceptance, circuit breakers, and an evaluation pipeline that catches regressions before they wake a tech at 3 AM.

Enterprise AI

OpenAI Frontier vs Anthropic Managed Agents: 2026 Comparison

Head-to-head: OpenAI Frontier and Anthropic's managed agent stack — strengths, fit, and what each means for enterprise AI voice and chat deployment.