Architecture
Understanding how zyn works internally.
System Overview
┌─────────────────────────────────────────────────────────────────────┐
│ Application │
│ │
│ synapse.Fire(ctx, session, input) │
└─────────────────────┬───────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Synapse │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Prompt │ │ Schema │ │ Validator │ │
│ │ Generator │ │ Generator │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────┬───────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Service Layer │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ pipz Pipeline │ │
│ │ ┌────────┐ ┌─────────┐ ┌──────────┐ ┌─────────┐ │ │
│ │ │ Retry │→│ Timeout │→│ Circuit │→│ Rate │→ ... │ │
│ │ │ │ │ │ │ Breaker │ │ Limiter │ │ │
│ │ └────────┘ └─────────┘ └──────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────┬───────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Provider │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ HTTP Client │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Request │ │ Response │ │ Token │ │ │
│ │ │ Builder │ │ Parser │ │ Counter │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────┬───────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ LLM API │
│ (OpenAI, Anthropic, etc.) │
└─────────────────────────────────────────────────────────────────────┘
Request Flow
1. Prompt Construction
When Fire() is called, the synapse builds a structured prompt:
// User calls
result, _ := classifier.Fire(ctx, session, "URGENT: Server down!")
// Internally, synapse builds:
{
"task": "What type of email is this?",
"input": "URGENT: Server down!",
"categories": ["spam", "urgent", "newsletter", "personal"],
"schema": { /* JSON schema for response */ }
}
2. Message Assembly
The prompt is combined with session history:
Messages sent to provider:
1. [system] Task description + schema + instructions
2. [user] Previous user message (from session)
3. [assistant] Previous response (from session)
4. [user] Current input
3. Pipeline Execution
The request flows through the pipz pipeline:
Request → Retry → Timeout → CircuitBreaker → RateLimit → Provider
│
Response ← Parse ← Validate ←─────────────────────────────────┘
4. Response Processing
// Provider returns raw JSON
{"primary": "urgent", "confidence": 0.95, "reasoning": [...]}
// Synapse:
// 1. Parses JSON into typed struct
// 2. Calls Validate() if applicable
// 3. Extracts the primary result
// 4. Updates session with user message + assistant response
// 5. Returns typed result
Component Details
Prompt Generation
Each synapse type generates specific prompts:
| Synapse | Prompt Structure |
|---|---|
| Binary | Task + input + boolean schema |
| Classification | Task + categories + input + category schema |
| ExtractT | Task + input + struct schema from T |
| Transform | Task + input + string output schema |
| AnalyzeT | Task + struct input + analysis schema |
| ConvertT,U | Task + input schema (T) + output schema (U) |
Schema Generation
Schemas are generated from Go types using reflection:
type Contact struct {
Name string `json:"name"`
Email string `json:"email" description:"Valid email address"`
}
// Generates JSON Schema:
{
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string", "description": "Valid email address"}
},
"required": ["name", "email"]
}
Service Layer
The Service[T] generic handles:
- Building the complete request
- Executing through the pipeline
- Parsing and validating responses
- Managing session updates
type Service[T Validator] struct {
provider Provider
prompt PromptBuilder
pipeline pipz.Chainable[*SynapseRequest]
hooks HookEmitter
}
Observability Hooks
Hooks fire at key points via capitan:
┌──────────────────┐
│ RequestStarted │ → Before pipeline execution
└────────┬─────────┘
│
┌────────▼─────────┐
│ ProviderStarted │ → Before HTTP call
└────────┬─────────┘
│
┌────────▼─────────┐
│ProviderCompleted │ → After successful HTTP call (includes tokens)
└────────┬─────────┘ OR
┌────────▼─────────┐
│ ProviderFailed │ → After HTTP failure
└────────┬─────────┘
│
┌────────▼─────────┐
│RequestCompleted │ → After successful parse/validate
└──────────────────┘ OR
┌──────────────────┐
│ RequestFailed │ → After pipeline exhaustion
└──────────────────┘ OR
┌──────────────────┐
│ResponseParseFailed│ → After parse/validation error
└──────────────────┘
Type Safety Guarantees
Compile-Time
- Generic synapses enforce input/output types
Validatorinterface required for custom types- Option functions are type-checked
Runtime
- JSON schema validation on LLM responses
Validate()called on parsed structs- Session operations bounds-checked
Next Steps
- Reliability Guide - Configure pipz patterns
- Observability Guide - Set up hooks
- Testing Guide - Test strategies