Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Memory

SearchHit

class SearchHit { string Content; decimal Score; Guid SourceEntityId; string SourceEntityType; string SourceLabel; string Section; int PageFrom; int PageTo; int MatchOffset; DateTime RememberedAt; MemoryOrigin Origin; MemoryLink[] Links; … }

One result of a semantic search — the matched text, how relevant it was, where it came from, when it was learned, and what its record connects to. Enough to quote it and say where the quote is from. Returned by Memory.Search as a List<SearchHit>. Available under `using Memory;`.

stable3 examples compiled by CIstoragesearchtype

Summary#

SearchHit is a single result of a semantic search: the text that matched, a relevance Score, the id of the entity the text came from, and the raw Distance. Memory.Search returns a List<SearchHit> ranked most-relevant-first. It is available under using Memory; and cannot be redeclared by the app.

Signature#

class SearchHit {
  // what matched
  string Content;          // the human-readable text that matched
  decimal Score;           // relevance — higher is more relevant
  decimal Distance;        // raw vector distance to the query — lower is closer
  MemoryKind Kind;         // a property chunk, a document section, a chat turn
  string? Via;             // the link's words, when `related:` reached this hit — null for a direct hit

  // which record
  Guid SourceEntityId;     // the entity instance this came from (a value, not a live reference)
  string SourceEntityType; // the NAME of the entity type that id belongs to
  string? SourceLabel;     // that record in words — its `semantic` render
  string? SourceDocument;  // the FILE the words are in, when it came from one

  // where, exactly
  string? Section;         // the heading this text sits under
  int? PageFrom;           // first page, when the source has pages
  int? PageTo;             // last page — "45-46" when it straddles a break
  int? SourceOffset;       // where this text starts in the source it was extracted from
  int? MatchOffset;        // where inside Content the match is
  int? MatchLength;        // how much of Content matched

  // when, and on whose say-so
  DateTime? RememberedAt;  // when this was learned
  MemoryOrigin Origin;     // Derived (the record restated) or Authored (somebody chose to remember it)
  Guid? AuthoredBy;        // who — null for Derived, always

  // what it connects to
  MemoryLink[] Links;      // what this record links to, and why
  int? LinksElided;        // how many links were left out for length
}

Description#

A SearchHit is a plain in-memory value describing why a piece of content matched, so you can rank, filter, or display results without re-fetching the source:

  • Content — the text that matched the query.
  • Score — relevance, higher is more relevant. When a keyword is supplied to the search, the score is the fused (hybrid) relevance; otherwise it is the semantic similarity.
  • SourceEntityType — the NAME of the entity type SourceEntityId belongs to. You need both: the id alone says which row a hit came from but not which table, so it cannot be loaded, grouped or linked on its own.
  • Kind — what kind of memory matched: a chunk of a [Searchable] property, a section of a document, a conversation turn. Worth reading when you build a prompt from hits — a recalled conversation turn and a stored field are different kinds of claim.
  • SourceEntityId — the id of the entity the content came from. It is a plain Guid value, not a live entity reference — use it to load the full entity when you need it.
  • Distance — the raw vector distance to the query embedding; lower is closer. Score is the value to rank on; Distance is exposed for tuning a threshold.
  • Via — why this hit is here at all, when Memory.Search's related: reached it by following a stated link instead of by being about one of the entities you named. It carries the link's words read in the direction you travelled, so a hit found through your deal reads "superseded by — renegotiated after the Q2 review" rather than the sentence the other end would read. Two hops read as a path ("… → …"), and a hit several links reached carries each of their reasons. It is empty for a direct hit, which is what lets you tell the two apart — and the hop is never folded into Score, because "similar to your query" and "linked to what you asked about" are different claims and one number cannot carry both.

Saying where it came from#

A quoted fact nobody can check is not much better than an unquoted one, so a hit carries everything needed to attribute it:

  • SourceLabel — the record in words, rendered from its semantic template: "Contract MSA-2024-11", beside the SourceEntityId a machine can act on. It is read fresh on every search, so renaming a record renames its citations immediately — nothing is re-indexed and nothing goes stale.
  • SourceDocument — the file the words are in, when the memory came from one. You usually want this and SourceLabel: "Order SO-1001, p. 45" cannot be checked when the order has four attachments.
  • Section — the heading the text sits under, when the source had headings.
  • PageFrom / PageTo — the pages, when the source has pages. A range, because text straddles page breaks.
  • SourceOffset — where the returned text begins in the source it was extracted from. The general form of the same question, for sources whose structure is not pages — a transcript's timeline, a source file's lines.
  • MatchOffset / MatchLength — where inside Content the match actually is. Content.Substring(MatchOffset, MatchLength) is the sentence that matched, which is what you want to quote or highlight when Content is a whole page. Empty when the hit is short enough to be its own match.

When, and on whose say-so#

  • RememberedAt — when this was learned, not when a row was written. Two memories often disagree, and the later one is usually right; without a date you cannot tell which is later.
  • OriginDerived means the record restating itself (a [Searchable] property's text, a document section); Authored means somebody chose to remember it (a conversation turn, a file, a stated link). Worth reading before you present a hit as fact: "the contract says" and "somebody said" carry different weight.
  • AuthoredBy — who, for authored memory. Null for Derived — asking who wrote a property's chunk is a category error, not a missing value.

Links carries what the hit's record is connected to, and why — each with the target's type, id and label, the relationship read in the right direction, and the link's own stated words. It is an ANNOTATION, not a second search: it describes what was already found, so it never changes the ranking and never crowds out a result. That is the difference between it and related:, which genuinely widens the search and is opt-in for that reason.

A record with hundreds of links does not empty them into an answer — the list is capped, and LinksElided says how many were left out. A link whose target you are not allowed to read is simply absent, and is never counted.

SearchHit is provided by the platform under using Memory;. Because Memory.Search's return contract depends on its exact shape, an app cannot declare its own type named SearchHit while the capability is in use — doing so is a compile error.

Examples#

using Osyrin.Memory;

string BestMatch(string q) {
  var hits = Memory.Search(q, limit: 3);   // already ranked best-first
  if (hits.Count == 0) { return ""; }
  return hits[0].Content;
}
using Osyrin.Memory;

string Cite(string question) {
  var hits = Memory.Search(question, limit: 1);
  if (hits.Count == 0) { return "nothing on file"; }

  var hit = hits[0];
  var where = hit.SourceLabel;
  if (hit.Section != null) { where = where + " - " + hit.Section; }
  if (hit.PageFrom != null) { where = where + ", p. " + hit.PageFrom; }
  return hit.Content + " (" + where + ")";
}
using Osyrin.Memory;

string SupersededBy(string question) {
  var hits = Memory.Search(question, limit: 1);
  if (hits.Count == 0) { return ""; }

  foreach (var link in hits[0].Links) {
    if (link.Label == "superseded by") { return link.TargetLabel + ": " + link.Reason; }
  }
  return "";
}

See also#

Related

using Memory (semantic search)

Opt into semantic (vector) search over your app's content. `using Memory;` adds a searchable store to the app…

Memory.Link / Memory.Unlink

State that two records are related, in your app's own words — "supersedes, because it was renegotiated after the Q2…