Arcana.Graph.CommunitySummarizer behaviour (Arcana v3.0.0)

Copy Markdown View Source

Behaviour for community summarization in GraphRAG.

Community summarizers generate natural language descriptions of entity communities. These summaries provide high-level context for global queries that need broad understanding rather than specific document chunks.

Built-in Implementations

Configuration

Configure your community summarizer in config.exs:

# Default: LLM-based summarization
config :arcana, :graph,
  community_summarizer: {Arcana.Graph.CommunitySummarizer.LLM, llm: &MyApp.llm/3}

# Disable summarization (communities won't have summaries)
config :arcana, :graph,
  community_summarizer: nil

# Custom module implementing this behaviour
config :arcana, :graph,
  community_summarizer: {MyApp.ExtractiveSum, max_sentences: 3}

# Inline function
config :arcana, :graph,
  community_summarizer: fn entities, relationships, _opts ->
    {:ok, "Community with #{length(entities)} entities"}
  end

Implementing a Custom Summarizer

Create a module that implements this behaviour:

defmodule MyApp.ExtractiveSum do
  @behaviour Arcana.Graph.CommunitySummarizer

  @impl true
  def summarize(entities, relationships, opts) do
    max_sentences = Keyword.get(opts, :max_sentences, 3)
    # Extract key sentences from entity descriptions...
    {:ok, summary}
  end
end

Summary Format

Summarizers should return a concise string (2-5 sentences) that:

  • Identifies the community's central theme or domain
  • Names the most important entities
  • Describes key relationships between entities

Summary

Callbacks

Generates a summary for a community.

Functions

Digest of the entities and relationships a summary was generated from.

The change-count threshold above which a clean summary is regenerated.

Checks if a community needs its summary regenerated.

Returns a map of fields to reset after regenerating a summary.

Generates a summary using the configured summarizer.

Callbacks

summarize(entities, relationships, opts)

@callback summarize(
  entities :: [map()],
  relationships :: [map()],
  opts :: keyword()
) :: {:ok, String.t()} | {:error, term()}

Generates a summary for a community.

Parameters

  • entities - List of entity maps with :name, :type, and optional :description
  • relationships - List of relationship maps connecting entities
  • opts - Options passed from the summarizer configuration

Returns

  • {:ok, summary} - The generated summary string
  • {:error, reason} - On failure

Functions

content_fingerprint(entities, relationships)

Digest of the entities and relationships a summary was generated from.

Membership alone can't answer "is this summary still accurate?": a summary is written from relationship text too, and ingesting another document adds relationships without moving anyone between communities. Storing this alongside the summary is what makes the question answerable later.

Covers the fields a summarizer is given: entity name, type and description, and relationship type and description. The built-in prompt only renders names and types, so a description edit can cost one redundant call - deliberate, since a custom summarizer is free to read descriptions, and over-refreshing is the safe direction. It never goes the other way.

default_threshold()

The change-count threshold above which a clean summary is regenerated.

Exposed so callers that predict what a summarize run will process (the dashboard's hint, for one) can apply the same rule.

needs_regeneration?(community, opts \\ [])

Checks if a community needs its summary regenerated.

Regeneration Triggers

  • dirty: true - Community was modified since last summary
  • change_count >= threshold - Many changes accumulated
  • summary: nil - No summary exists yet
  • summary_fingerprint: nil - Summary predates fingerprinting, so there is no way to tell whether it still matches the graph. It regenerates once, the run records a fingerprint, and it settles from then on

Options

  • :threshold - Number of changes before regeneration (default: 10)

reset_change_tracking()

Returns a map of fields to reset after regenerating a summary.

Use with Ecto.Changeset.change/2 to mark a community as clean:

community
|> Community.changeset(CommunitySummarizer.reset_change_tracking())
|> Repo.update()

summarize(entities, relationships, opts \\ [])

Generates a summary using the configured summarizer.

The summarizer can be:

  • A {module, opts} tuple where module implements this behaviour
  • A function (entities, relationships, opts) -> {:ok, summary} | {:error, reason}

  • nil to skip summarization (returns empty string)

Falls back to LLM summarizer if not configured but :llm option is provided.