Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Neumenon/glyph/llms.txt
Use this file to discover all available pages before exploring further.
Minimal (No Schema)
Use when you trust the model and want minimal overhead:
# System prompt
TOOLS = """
Available tools:
- search{query:str max_results:int} - Search the web
- calculate{expression:str} - Evaluate math
- browse{url:str} - Fetch webpage content
"""
# Parse tool call from model output
tool_call = glyph.parse(model_output)
name = tool_call.type_name # "search"
args = tool_call.fields # {"query": "weather NYC", "max_results": 10}
With Full Validation
Recommended for production systems:
registry = glyph.ToolRegistry()
registry.register("search", {
"query": {"type": "str", "required": True, "min_len": 1},
"max_results": {"type": "int", "min": 1, "max": 100, "default": 10},
})
registry.register("calculate", {
"expression": {"type": "str", "required": True},
})
registry.register("browse", {
"url": {"type": "str", "required": True, "pattern": r"^https?://"},
})
# Validate before execution
result = registry.validate(tool_call)
if not result.valid:
return f"Invalid tool call: {result.errors}"
System Prompt Pattern
Clear, concise tool documentation for LLMs:
You have access to these tools:
search{query:str max_results:int[1..100]=10}
Search the web. Returns list of results.
calculate{expression:str}
Evaluate a mathematical expression. Returns number.
browse{url:str}
Fetch and summarize a webpage. Returns text.
To use a tool, output:
ToolName{arg1=value arg2=value}
Example:
search{query="python async tutorial" max_results=5}
Agent Patterns
ReAct Loop
Reason and Act pattern with GLYPH state:
async def react_loop(goal: str, max_turns: int = 10):
state = {"goal": goal, "observations": [], "turn": 0}
for turn in range(max_turns):
# Format state as GLYPH (compact)
state_glyph = glyph.from_json(state)
prompt = f"""
State: {state_glyph}
Think step by step, then either:
1. Use a tool: ToolName{{args}}
2. Return final answer: Answer{{result="..."}}
"""
response = await llm.generate(prompt)
parsed = glyph.parse(response)
if parsed.type_name == "Answer":
return parsed.fields["result"]
# Execute tool
result = await execute_tool(parsed.type_name, parsed.fields)
state["observations"].append({
"tool": parsed.type_name,
"args": parsed.fields,
"result": result,
})
state["turn"] += 1
raise MaxTurnsExceeded()
Multi-Agent Coordination
Using stream IDs for agent communication:
# Coordinator assigns SIDs
PLANNER_SID = 1
EXECUTOR_SID = 2
CRITIC_SID = 3
# Planner sends task
writer.write_frame(
sid=EXECUTOR_SID,
seq=0,
kind="doc",
payload=glyph.emit(glyph.struct("Task",
action="search",
query="latest AI news",
))
)
# Executor sends result
writer.write_frame(
sid=PLANNER_SID,
seq=0,
kind="doc",
payload=glyph.emit(glyph.struct("Result",
task_id=1,
status="complete",
data=search_results,
))
)
# Critic sends feedback
writer.write_frame(
sid=EXECUTOR_SID,
seq=1,
kind="doc",
payload=glyph.emit(glyph.struct("Feedback",
task_id=1,
score=0.8,
suggestion="Include source URLs",
))
)
Checkpoint / Resume Pattern
Safe state persistence:
def save_checkpoint(agent_state, path: str):
with open(path, "w") as f:
f.write(glyph.emit(agent_state))
def load_checkpoint(path: str):
with open(path) as f:
return glyph.parse(f.read()).value
# Save periodically
if turn % 5 == 0:
save_checkpoint(state, f"checkpoint_{turn}.glyph")
# Resume from crash
state = load_checkpoint("checkpoint_latest.glyph")
State Management
Simple: Full State Per Message
Best for short sessions:
# Agent state as GLYPH
state = glyph.struct("AgentState",
goal="Find weather in NYC",
memory=[
{"query": "NYC weather", "result": "72F sunny"},
],
turn=3,
)
# Include in context
context = f"""
Current state:
{glyph.emit(state)}
Continue toward the goal.
"""
Advanced: Patches with Verification
Optimal for long-running agents:
from glyph import stream
# Initial state
writer.write_frame(sid=1, seq=0, kind="doc", payload=glyph.emit(state))
# After each action, send patch
patch = glyph.patch([
("=", "turn", 4), # Set value
("+", "memory", new_memory_entry), # Append
("~", "token_count", tokens_used), # Increment
])
# Include base hash for safety
base_hash = stream.state_hash(current_state)
writer.write_frame(
sid=1,
seq=1,
kind="patch",
payload=patch,
base=base_hash, # Receiver rejects if state diverged
)
Progress Reporting
Non-intrusive status updates:
# Send progress during long operations
writer.write_frame(
sid=1,
seq=seq,
kind="ui",
payload=glyph.emit(glyph.struct("Progress",
pct=0.45,
msg="Processing batch 9 of 20",
eta_seconds=120,
))
)
# Client handles UI updates
@handler.on_ui
def handle_ui(sid, seq, payload, state):
event = glyph.parse(payload)
if event.type_name == "Progress":
update_progress_bar(event.fields["pct"], event.fields["msg"])
Anti-Patterns
Don’t: Parse with Regex
# BAD - breaks on nested structures
match = re.search(r'search\{query="([^"]+)"', response)
Don’t: Validate After Full Generation
# BAD - wastes tokens on invalid calls
response = await llm.generate(prompt) # 50 tokens
result = glyph.parse(response)
if result.type_name not in allowed_tools: # Discovered too late
raise Error()
Don’t: Send Full State Every Turn
# BAD - O(n) tokens per turn for n observations
state["observations"].append(new_obs)
send_full_state(state) # Gets bigger every turn
Don’t: Inline Large Data
# BAD - bloats context
state = {"embeddings": [[0.1, 0.2, ...] * 1536] * 100} # Huge
Best Practices
Streaming Validation Decision Tree
| Condition | Action |
|---|
| Unknown tool name | Cancel at token ~3-5 |
| Wrong argument type | Cancel when type detected |
| Constraint violation | Cancel when value complete |
| Missing required arg | Wait until } then error |
Token Efficiency Guidelines
High-value scenarios for GLYPH:
- System prompts with tool definitions (sent every request)
- Conversation history (grows over time)
- Batch operations (thousands of records)
- Multi-turn agents (state persisted across calls)
Token savings by structure:
| Pattern | JSON | GLYPH | Savings |
|---|
| Tool call | 42 | 28 | 33% |
| Tool list (5 tools) | 180 | 95 | 47% |
| Agent state (small) | 120 | 75 | 38% |
| Agent state (large) | 500 | 290 | 42% |
| Tabular data (10 rows) | 320 | 145 | 55% |
State Management Guidelines
Use full state when:
- Sessions are short (< 10 turns)
- State is small (< 1KB)
- Simplicity is more important than efficiency
Use patches when:
- Long-running sessions (> 10 turns)
- State grows over time
- Multiple agents share state
- Network bandwidth is limited
Error Handling
Graceful degradation:
try:
result = glyph.parse(llm_output)
except ParseError:
# Fall back to extracting data with LLM
result = extract_structured_data(llm_output)
Progressive validation:
# Validate what you can, execute what's valid
if result.partial_valid:
# Execute with defaults for missing fields
execute_with_defaults(result.tool_name, result.fields)
else:
# Full validation failure
request_clarification()