Principle:Langfuse Langfuse Prompt Dependency Graph
| Knowledge Sources | |
|---|---|
| Domains | Prompt Management, Dependency Resolution |
| Last Updated | 2026-02-14 00:00 GMT |
Overview
The Prompt Dependency Graph is a composability mechanism that allows prompt templates to reference and embed other prompts by name, forming a directed acyclic graph (DAG) that is resolved at read time by recursively inlining dependent prompt content.
Description
Complex LLM applications often require prompts that share common instructions, persona definitions, or output format specifications. Rather than duplicating this content across multiple prompts (leading to drift and inconsistency), the Prompt Dependency Graph enables authors to compose prompts from reusable building blocks.
A prompt can embed a reference to another prompt using a special tag syntax:
@@@langfusePrompt:name=PromptName|version=2@@@ @@@langfusePrompt:name=PromptName|label=production@@@
These tags are parsed from the prompt content and stored as dependency records. When a prompt is retrieved, the system traverses the dependency graph, fetches each referenced prompt, and replaces the tags with the resolved content. This creates a fully expanded, self-contained prompt ready for LLM consumption.
The problems solved by this principle include:
- Content reuse: Shared instructions (e.g., formatting rules, persona definitions) can be maintained in a single prompt and referenced from many others.
- Consistency: When the shared prompt is updated, all dependents automatically receive the updated content upon their next resolution (when referenced by label) or retain their pinned version (when referenced by version number).
- Separation of concerns: Authors can decompose complex system prompts into manageable, testable units.
Usage
Use the Prompt Dependency Graph when:
- Multiple prompts share common preamble, instructions, or output format definitions.
- You want to update shared instructions in one place and have all dependent prompts pick up the change (via label references).
- You need to pin a dependency to a specific version for stability (via version references).
- You are building a library of reusable prompt components for a team or organization.
Theoretical Basis
The dependency resolution follows a depth-first recursive graph traversal with cycle detection and a bounded nesting depth.
Tag Parsing
Dependency tags are extracted from the JSON-stringified prompt content using a regular expression:
PATTERN = /@@@langfusePrompt:(.*?)@@@/g
FUNCTION parseDependencyTags(content):
stringified = JSON.stringify(content)
matches = findAllMatches(stringified, PATTERN)
uniqueMatches = DEDUPLICATE(matches)
validTags = []
FOR EACH match IN uniqueMatches:
innerContent = stripDelimiters(match)
parts = split(innerContent, "|")
// Must have exactly 2 parts, first must be name=
IF length(parts) != 2 OR NOT startsWith(parts[0], "name="):
CONTINUE
params = parseKeyValuePairs(parts)
IF params.version EXISTS:
tag = { name: params.name, type: "version", version: toNumber(params.version) }
ELSE IF params.label EXISTS:
tag = { name: params.name, type: "label", label: params.label }
ELSE:
CONTINUE
IF isValid(tag):
validTags.APPEND(tag)
RETURN validTags
Graph Resolution
CONSTANT MAX_NESTING_DEPTH = 5
FUNCTION buildAndResolvePromptGraph(projectId, parentPrompt, dependencies):
graph = {
root: { name: parentPrompt.name, version: parentPrompt.version, id: parentPrompt.id },
dependencies: {}
}
seen = EMPTY_SET
FUNCTION resolve(currentPrompt, deps, level):
// Guard: nesting depth
IF level >= MAX_NESTING_DEPTH:
RAISE error("Maximum nesting depth exceeded")
// Guard: circular dependency (by ID or by name matching the root)
IF currentPrompt.id IN seen
OR (currentPrompt.name == parentPrompt.name AND currentPrompt.id != parentPrompt.id):
RAISE error("Circular dependency detected for " + currentPrompt.name)
seen.ADD(currentPrompt.id)
// Retrieve or use passed dependencies
IF deps IS NULL:
deps = fetchDependenciesFromDB(projectId, currentPrompt.id)
IF deps IS NOT EMPTY:
resolvedContent = JSON.stringify(currentPrompt.prompt)
FOR EACH dep IN deps:
depPrompt = fetchPrompt(projectId, dep.name, dep.version OR dep.label)
IF depPrompt IS NULL:
RAISE error("Dependency not found: " + dep.name)
IF depPrompt.type != "text":
RAISE error("Only text prompts can be dependencies: " + dep.name)
// Record in adjacency list
graph.dependencies[currentPrompt.id].APPEND(depPrompt)
// Recurse to resolve nested dependencies
resolvedDepContent = resolve(depPrompt, NULL, level + 1)
// Replace all matching tags (version and label variants)
resolvedContent = replaceAllTags(resolvedContent, depPrompt, resolvedDepContent)
seen.REMOVE(currentPrompt.id)
RETURN JSON.parse(resolvedContent)
ELSE:
seen.REMOVE(currentPrompt.id)
RETURN currentPrompt.prompt
resolvedPrompt = resolve(parentPrompt, dependencies, 0)
RETURN {
graph: IF graph.dependencies IS NOT EMPTY THEN graph ELSE NULL,
resolvedPrompt: resolvedPrompt
}
Key constraints:
- Maximum nesting depth: The graph traversal is bounded to 5 levels to prevent runaway recursion and ensure predictable performance.
- Cycle detection: A seen set tracks visited prompt IDs during traversal. If a prompt ID is encountered again, or if a dependency references the parent prompt name (even at a different version), the system raises a circular dependency error.
- Text-only dependencies: Only prompts of type "text" can be referenced as dependencies. Chat-type prompts cannot be inlined because their structured message format is incompatible with string substitution.
- Tag format strictness: Tags must have exactly two pipe-delimited parts with name as the first key. Malformed tags are silently skipped rather than causing errors.