-
Notifications
You must be signed in to change notification settings - Fork 0
Getting Started
This guide introduces the core concepts of FML (Frags Modeling Language) and walks you through authoring and structuring your first agentic execution plan.
FML is a declarative, domain-specific programming language designed to orchestrate complex, multi-stage LLM agent pipelines. While traditional agent prompts often mix tool calling, reasoning, context formatting, and output structuring into a single massive, fragile prompt, FML breaks the workflow into a deterministic directed acyclic graph (DAG) of sessions.
When prompting frontier models like Gemini 3 Pro to execute complex tasks:
- Tool Pollution & Hallucination: Asking an LLM to call tools while simultaneously producing a strict JSON schema frequently results in malformed JSON or unnecessary tool calls.
- Context Bloat: Dumping massive API payloads directly into the model context window wastes tokens and degrades reasoning quality.
- Lack of Determinism: Pure LLM chains can fail unpredictably when simple transformations (such as array filtering, ID extraction, or API piping) could be executed deterministically.
FML solves these issues by providing:
-
Phased Execution: Enforces tool calls strictly during Pre-Prompts (
+), and disables tools completely during Prompts (-) for flawless schema formatting. - Deterministic PreCalls: Allows running native tools and sandboxed JavaScript transformations before the LLM ever sees the data.
-
Rigid Namespace Scoping: Keeps parameter declarations (
params), runtime variables (vars), and session outputs (context) isolated.
An FML file consists of two primary layers:
┌─────────────────────────────────────────────────────────┐
│ Global Scope │
│ • System Prompt (`system`) │
│ • Parameters (`parameter`) │
│ • Global Variables (`set`) │
│ • External Tool Dependencies (`require`) │
│ • Reusable Components (`components`) │
│ • Global PreCalls (`call`) │
└────────────────────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Session DAG (Execution Graph) │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ session("ingest") │ │
│ │ 1. PreCall: Fetch external data │ │
│ │ 2. Pre-Prompt (+): Tool use & information gathering│
│ │ 3. Prompt (-): JSON structuring │ │
│ │ 4. Schema validation │ │
│ └────────────────────────┬────────────────────────┘ │
│ │ context.ingest │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ session("analyze", after="ingest") │ │
│ │ 1. Context Injection │ │
│ │ 2. Prompt (-): Evaluation & Analysis │ │
│ │ 3. Schema validation │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
- Global Configuration: Defines the environment, system persona, global input parameters, and required tool integrations (such as Model Context Protocol / MCP servers).
-
Sessions: Independent execution units that run either sequentially or as part of a dependency graph defined by
after="upstream_session".
Let's build a practical FML plan: An Automated Competitive Intelligence Pipeline.
Every plan can declare a global system prompt and typed parameter inputs that users supply at runtime:
# Set global instruction for all sessions
system(`You are a senior tech analyst. Provide concise, fact-checked analysis.`)
# Declare input parameters with type and defaults
parameter("competitor", type="string", default="Acme Corp")
parameter("depth", type="string", default="standard", enum=brief|standard|deep)
Note
parameter supports types string, int, and bool. The enum=val1|val2 attribute restricts allowable values.
Tools such as MCP servers or internal databases are declared at the root level using require:
# Declare external tool integrations
require mcp market_feed
Important
Global require statements are strictly single-line declarations. Never provide configuration bodies or allowlists at the root level.
In the components block, define schemas that can be referenced across multiple sessions using $ComponentName:
components {
schema("NewsItem") {
headline: string # Article headline
source: string # Publication name
impact: low|medium|high # Assessed business impact
}
}
Session 1 uses tools to find information. Notice the use of + (Pre-prompt) where tool calling is permitted:
session("gather_news", target="news_data") {
# Activate tools specifically needed for this session
use mcp market_feed { allowlist = ["search_headlines", "get_article"] }
# Phase 2: Pre-prompt (Tool use ENABLED)
+ Search market feeds for recent developments regarding {{ .params.competitor }}.
Collect the top 3 most impactful news items.
# Phase 3: Prompt (Tool use DISABLED - Schema mapping only)
- Structure the gathered news items into the output schema.
# Phase 4: Schema validation
schema {
company: string
items: $NewsItem[]
}
}
Session 2 depends on Session 1. It declares after="gather_news", accesses context.news_data, and produces the final report:
session("synthesize_report", after="gather_news") {
# Inject context from Session 1 formatted as JSON
context "News gathered: {{ .context.news_data | json }}"
# Prompt (No tool calling needed, pure analytical synthesis)
- Based strictly on the gathered news items, summarize the competitor's
strategic trajectory and assign an overall threat level.
schema {
summary: string # Analytical summary
threat_level: low|medium|high # Competitive threat level
recommended_countermeasures: string[]
}
}
When the FML runtime executes this plan:
-
Parameter Resolution: Verifies that
"competitor"and"depth"match their declared types and constraints. -
Session Graph Construction: Identifies that
synthesize_reportdepends ongather_newsviaafter. -
Execution of
gather_news:- Pre-execution phase runs any deterministic
callstatements (none in this session). - Phase 2 executes the
+pre-prompt. Gemini 3 Pro receives the tool definitions fromuse mcp market_feedand executes tool calls until it has gathered the necessary headlines. - Phase 3 strips all tool definitions from the LLM and runs the
-prompt. Gemini structures the raw data into JSON. - Phase 4 validates the JSON against
schema { company: string, items: $NewsItem[] }. - The validated JSON is published under
context.news_data.
- Pre-execution phase runs any deterministic
-
Execution of
synthesize_report:- The
contextblock interpolatescontext.news_datausing Go template JSON serialization (| json). - The prompt is evaluated and validated against the schema.
- Plan terminates with the final output.
- The
-
+vs-: Use+for doing work (calling tools, researching, drafting). Use-for formatting into JSON. -
Dependencies: Always specify
after="upstream_session"when referencingcontext.upstream_session. -
Namespaces: Reference parameters via
{{ .params.name }}in templates andparams.namein expressions. - Safety: The runtime strictly validates types and prevents tool calling during final schema generation.
- Read the Language Overview to learn about FML syntax and lexical rules.
- Dive into Sessions & Lifecycle for an exhaustive look at execution phases.
- Explore Calls & PreCalls to run deterministic JavaScript and tool pipelines.
FML (Frags Modeling Language) | Getting Started | Cheat Sheet | Examples
Documentation for FML & Gemini Agent Workflows — Maintained by Frags HQ