Samuel Agbede / An open notebookSay hello ↗
← Back to notes

Note 20 Oct 2026 15 min read

Why LinkedIn Gave Its Hiring Assistant a Memory Tree

I’ve been looking at different memory architectures for AI agents. LinkedIn’s Hiring Assistant is the first one I’ve come across that organizes memory into a hierarchical tree. I wanted to understand how it works and why they chose that structure.


Before we get into the architecture, let’s understand the problem LinkedIn is trying to solve.

Imagine you’re a recruiter hiring a forward-deployed engineer. In LinkedIn’s terminology, this search is a hiring project. It brings together the requirements, candidates and activity for that particular role.

The job description says:

“5+ years experience in Python and distributed systems.”

And you have 10 candidates.

How do you actually filter them?

You might have a few considerations that aren’t expressly declared in the job description:

  • prefer someone who has worked at a startup
  • compromise on years of experience if they have unusually strong distributed-systems experience
  • consider Go experience acceptable even though the job description says Python

Quite often these considerations live in the recruiter’s head. Some emerge while they are working through candidates.

Now imagine another search six months later, this time for a backend engineer.

Which of those preferences apply to this new search?

Perhaps accepting Go was specific to the first role. The team already used Go, so it made sense there.

But the recruiter’s preference for systems depth might show up across several searches. That could be useful to remember when they start another hiring project.

And some requirements might come from the organization itself, applying across recruiters and roles.

When the assistant starts a new search, it needs to work out which previous preferences are relevant.

Some belong to a particular project. Others describe the recruiter more generally. Some apply across the organization.

How does LinkedIn organize memory so it can make use of those differences?

The memory tree

In their paper, Hierarchical Long-Term Semantic Memory for LinkedIn’s Hiring Agent, LinkedIn organizes memory around three entities already present in its product:

  • organization
  • recruiter seat, meaning the recruiter’s account
  • hiring project

For example, imagine SamCorp has three recruiters, and each recruiter has two hiring projects.

Figure 2: SamCorp’s memory tree contains recruiter nodes for Alice, Bob and Carol, each with hiring projects as leaves.

Each project holds memories from that particular search. The recruiter node brings together information from their projects. The organization node brings together information across recruiters.

If I want to know what mattered for one backend role, there is a project memory to look at.

If I want to know what Alice generally prioritizes, there is somewhere to capture patterns across her searches.

How does it learn those preferences?

We now have somewhere to store these memories. But where do they come from?

A recruiter won’t necessarily write down every preference. Some become apparent through what they do during a search.

The project is the leaf of this tree. This is where candidate interactions, qualification changes and feedback feed into memory.

LinkedIn’s Hiring Assistant learns from both explicit and implicit signals.

Some signals are explicit. A recruiter changes a requirement or writes down why a candidate is suitable.

Others are implicit. Repeatedly messaging candidates with distributed-systems experience might suggest that systems depth matters.

Figure 1: Declared job requirements and preferences learned from recruiter interactions both inform candidate evaluation.

Those interactions provide evidence from which the system derives the project’s memory.

Figure 3: Candidate interactions and project changes feed the project leaf, from which facets, QA pairs and a summary are derived.

From there, we can look at what gets passed up the tree.

How do the higher-level memories work?

Each higher-level node contains a summarized representation of the nodes beneath it.

At the recruiter level, that means looking for patterns across several projects.

Suppose Alice has backend, ML and platform engineering projects.

For backend and platform roles, she is flexible about the exact language but cares a lot about systems depth. For the ML role, Python is mandatory and research experience matters.

At the project level, those distinctions remain intact.

What would we want the recruiter-level summary to preserve here?

Probably that Alice cares about different things for different kinds of roles. A single statement that she prefers Python would miss that.

Figure 4: Three project memories aggregate into Alice’s recruiter summary while preserving different preferences for backend and ML roles.

This is useful because users don’t always ask questions at the same level of abstraction.

Sometimes the relevant question is:

“What did I want for this particular backend role?”

That maps naturally to the project level.

Other times the useful question is:

“What do I generally tend to prioritize when hiring backend engineers?”

That requires synthesizing information across several previous projects.

The tree gives the system retrieval units at multiple levels of abstraction.

Why use a schema-aligned tree?

Once we decide to organize memory hierarchically, there’s another question.

How do we decide what belongs together?

We could group information by similarity. Or we could use relationships the application already knows about.

There are other hierarchical retrieval approaches, such as RAPTOR.

RAPTOR creates a hierarchy by embedding chunks, clustering semantically similar chunks, summarizing those clusters and recursively repeating the process.

The topology is discovered from the data.

LinkedIn’s topology already exists: organization, recruiter, project.

The product already knows that Project A belongs to Alice.

If Project A changes significantly, it is still Project A, it still belongs to Alice, and Alice still belongs to SamCorp.

The structure stays stable as the contents of those memories change.

That separation is quite elegant:

The application schema decides where the information belongs.

The memory system decides what the information means.

Figure 8: RAPTOR discovers a hierarchy through semantic clustering; LinkedIn uses an existing organization, recruiter and project schema.

This also gives the system a way to track which higher-level memories depend on a project. We’ll see why that matters when we get to updates.

Every node has three representations

Having a memory at the right scope is useful. We still need to find it when someone asks a question.

The question may be phrased quite differently from the information we stored.

LinkedIn gives each node three representations that can be searched.

The first is facets: structured key-value facts.

The second is QA pairs: synthetic questions and answers that the node should be able to answer.

The third is a summary: a concise natural-language description of the node.

The motivation here is that different queries may match different representations better.

Figure 5: One recruiter node has three representations: structured facets, synthetic question-answer pairs and a natural-language summary.

How does search work?

Say a recruiter starts a new project to hire an ML engineer.

How does the agent know whether it should search project-level memories, recruiter-level memories or organization-level memories?

Interestingly, LinkedIn doesn’t appear to explicitly classify the query as “broad” or “specific” and then route it to one level.

It first limits the search space to the relevant identity scope, for example the recruiter’s permitted subtree.

Then nodes from different levels are allowed to compete during retrieval.

Figure 6: An identity filter restricts retrieval to Alice’s subtree before recruiter and project memories compete in semantic search.

The three representations are searched independently.

The retrieval itself is embedding-based.

For summaries, the user query is embedded and compared with stored summaries.

For QA, the user query is embedded and compared against the synthetic questions associated with each node.

For facets, the query is decomposed into smaller structured semantic queries and compared with stored facet representations.

The top-K results from the three retrievers are then combined and passed to the downstream LLM.

But what happens if those results include memories that sound relevant without actually helping answer the question?

I’ll come back to that after looking at how the memories are kept up to date.

How does indexing work on the memory tree?

So far, we’ve looked at searching memories that already exist.

But the recruiter is still working. They message another candidate, change a requirement or give new feedback.

What needs to be updated when that happens?

When new information appears in a recruiter’s project, the project node is the first semantic-memory node affected.

The project memory can then be regenerated or reconciled from the latest underlying source data.

That means recomputing its facets, QA pairs and summary.

Once the project leaf has changed, its ancestors are recomputed.

Figure 7: A changed backend project refreshes only itself, Alice and SamCorp: three of ten nodes.

If one project belonging to Alice changes, Bob’s projects and recruiter-level memory don’t need to be touched.

Only the affected path needs to be refreshed.

This is where the stable hierarchy becomes useful again. The system already knows which recruiter and organization memories depend on that project.

LinkedIn reports a full production rebuild taking on the order of days, while incremental indexing dramatically reduces both runtime and LLM calls because only changed leaves and their ancestors need to be processed.

What about stale memories?

That update process also gives us a way to deal with some stale memories.

Suppose a project originally says “Python mandatory”.

The recruiter later changes it to “Python preferred, Go acceptable”.

The project node can be recomputed from the updated requirements.

Its recruiter-level and organization-level ancestors can then also be regenerated.

Figure 9: Changing Python mandatory to Python preferred, Go acceptable refreshes project memory and ancestor summaries.

So the old Python requirement doesn’t have to stay in memory after the recruiter has changed it.

But what if the source data hasn’t been edited, and the recruiter’s preferences have changed since then?

For example, imagine a recruiter strongly preferred startup candidates in 2024.

In 2025, that preference was still there.

But by 2026, it no longer seems to matter.

Those old projects may still be factually correct, but they may no longer represent the recruiter’s current preferences.

This is a different problem from keeping memory synchronized with edited source data.

I would have liked to see more discussion of recency weighting, decay or temporal confidence in the paper.

Privacy and identity boundaries

LinkedIn also points out that this schema alignment makes access control straightforward.

A recruiter’s query can be restricted to the part of the hierarchy they are allowed to access.

This is the identity filter we saw in retrieval. It selects the allowed subtree before anything semantic happens, keeping memories outside that scope out of the results.

However, I’m not fully convinced that the tree itself is necessary to get this property.

A flat memory record could simply contain org_id, recruiter_id and project_id, and retrieval could apply a hard metadata filter before doing anything semantic.

So I see schema-aligned topology as a convenient way of encoding ownership and dependencies. Metadata filters could enforce those access boundaries too.

If metadata can handle the access boundaries, could it also support the other things we want from this memory?

I’m not convinced this needs to be a tree

Imagine extracting long-term semantic memories as independent entries.

Each memory could carry its text, the recruiter and project it belongs to, a timestamp, and the evidence it came from. You could also attach metadata such as organization and role family.

Then imagine maintaining separate summaries.

One for each project. One for each recruiter. One for the organization.

Or perhaps one for each combination of recruiter and role family.

These would be materialized views: summaries you compute ahead of time because you expect them to be useful.

Retrieval could first use metadata to constrain scope, retrieve relevant entries and summaries, rerank them and then send the final context to the LLM.

Figure 10: An alternative design combines semantic memory entries, metadata filtering and materialized summaries before reranking.

This seems to give you many of the same properties:

  • privacy through metadata filtering
  • project-level detail
  • recruiter-level aggregation
  • organization-level aggregation
  • incremental invalidation
  • multiple levels of abstraction

The main additional complexity is dependency tracking.

If Project A changes, you need to know which materialized summaries depend on it.

The tree gives you that dependency structure for free.

Project A belongs to Alice. Alice belongs to SamCorp.

So if Project A changes, you know which path to refresh.

But a flat structure may be more flexible because hiring data is not naturally one-dimensional.

A project might belong to Alice, but also be about backend engineering, in EMEA, at staff level, in fintech.

Those are all potentially useful ways of grouping the same information.

The useful aggregation for a query might be:

“What does Alice normally prioritize for staff backend roles in EMEA?”

There may be no single tree node corresponding to that combination.

A metadata-driven system could generate or cache that view dynamically.

Perhaps a useful way to describe this is:

semantic memories + metadata + materialized semantic views

with a tree being one particular way of organizing those views.

Do we need all three representations?

There is another thing I’m curious about in the results.

We’ve seen that each node stores facets, QA pairs and a summary. But how much does each representation contribute?

In Table 3 of the paper, summaries alone get a correctness score of 0.775 on summary-style questions. The full system gets 0.798.

For retrieval-style questions, the F1 scores are even closer: 0.707 for summaries alone and 0.712 for all three representations.

So summaries seem to be doing most of the work here.

The combined system does score higher on those measures. Still, I wonder whether that improvement justifies generating, storing and searching all three representations.

Which questions benefit enough from facets or QA pairs to make them worth keeping?

If I were building something similar, I’d want to start with summaries and look at the questions they struggle with. That would give me a clearer reason to add another representation.

The retrieval fusion feels surprisingly simple

The other thing I find interesting is how retrieval from the three representations is combined.

We saw earlier that each retriever returns its top-K results. Those candidates are then combined before being sent to the downstream LLM.

There doesn’t appear to be a strong second-stage referee asking:

“Does this memory actually answer the user’s question?”

What could go wrong here?

Imagine the recruiter asks:

“What kind of things do I normally look for?”

The recruiter-level summary might be exactly what we want.

But a project-level memory might describe what mattered for just one search. It could be relevant to that project without telling us much about what the recruiter normally wants.

Both could enter the final context, leaving the downstream LLM to work out which one helps.

The figure below includes a few hypothetical examples, including a company called “Normally” that might produce a misleading match. These are illustrations of the concern, not failures reported in the paper.

Figure 11: An illustrative union of facet, QA and summary results contains useful memories and irrelevant matches, leaving the LLM to judge relevance.

This is where I wonder whether a decision model such as Jev could be useful.

Embeddings could first retrieve a set of candidate memories. Say we get 20.

Then we ask a more direct question about each one:

“Does this memory actually help answer this query?”

Perhaps only three pass that check. Those are the ones we send to the LLM.

Final thought

The part of LinkedIn’s design I find most convincing is the decision to precompute semantic abstractions at different scopes.

A project memory captures what happened during one search.

A recruiter memory captures patterns across many searches.

An organization memory captures broader patterns across recruiters.

That allows the system to answer questions at different levels of granularity without repeatedly asking an LLM to reason over every historical interaction from scratch.

The open question for me is whether the tree is the best general abstraction for doing this, or simply a particularly convenient one for LinkedIn’s domain.

Figure 12: Raw interactions become semantic memories and precomputed abstractions, retrieved at the appropriate scope for a new hiring task.

Which summaries are useful enough to compute ahead of time?

How do we keep them up to date as the recruiter’s preferences change?

And when a new task comes along, how do we retrieve the ones that actually help?

Those are questions I want to keep in mind as I look at other memory architectures.

Do you have any thoughts, corrections or questions? I'd love to hear from you.

Reply by email samuelagbede@outlook.com