Skip to content
Learn Agentic AI15 min read0 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.

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.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

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.

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 this article
C

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

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