Skip to main content

Overview

This guide outlines extensible patterns for customizing Shannon while maintaining upgrade compatibility and clean separation of concerns.

Templates

System 1 - Pre-built workflows with low overhead

Tools

Add capabilities via MCP, OpenAPI, or Python

Vendor Adapters

Domain-specific integrations without core changes

Synthesis Templates

Customize research output formatting

Extension Methods Comparison

Extension TypeComplexityCode ChangesUse Case
TemplatesLowYAML onlyRepeatable workflows
MCP/OpenAPI ToolsLowConfig onlyExternal APIs
Built-in ToolsMediumPython onlyCustom logic
Vendor AdaptersMediumPython + ConfigDomain-specific integrations
DecompositionHighGo + PythonCustom planning logic
For most use cases, Templates and Vendor Adapters provide the best balance of power and simplicity.

Extend Decomposition (System 2)

For custom planning and reasoning logic The orchestrator calls the LLM service endpoint /agent/decompose for planning.

When to Use

  • Custom task decomposition strategies
  • Domain-specific planning heuristics
  • Pre/post-processing of LLM requests
  • Integration with external planning systems

Implementation Options

Best for: Pre/post-processing LLM requestsAdd heuristics to go/orchestrator/internal/activities/decompose.go:
// Example: Pre-process query before decomposition
func PreprocessQuery(query string) string {
    // Add domain-specific context
    // Normalize input format
    // Inject additional constraints
    return enhancedQuery
}

// Example: Post-process decomposition response
func PostprocessDecomposition(resp *DecompositionResponse) {
    // Validate subtasks
    // Add fallback steps
    // Optimize execution order
}
Keep response schema compatible with DecompositionResponse to avoid breaking orchestrator workflows.

Add/Customize Templates (System 1)

For repeatable workflows with low overhead

When to Use

  • Predefined workflows (data analysis, code review, etc.)
  • Quick task execution without AI planning
  • Common patterns used frequently
  • Performance-critical paths

Creating Templates

1

Create Template File

Place templates in your own directory:
# templates/custom/research-workflow.yaml
name: "research_workflow"
description: "Multi-stage research and analysis workflow"
version: "1.0.0"

extends: []  # Optional: inherit from other templates

defaults:
  model_tier: "medium"
  budget_agent_max: 5000
  require_approval: false

nodes:
  - id: "search"
    type: "simple"
    strategy: "react"
    tools_allowlist: ["web_search", "calculator"]
    budget_max: 1000
    depends_on: []

  - id: "analyze"
    type: "cognitive"
    strategy: "chain_of_thought"
    tools_allowlist: ["python_executor", "calculator"]
    budget_max: 2000
    depends_on: ["search"]

  - id: "synthesize"
    type: "simple"
    strategy: "react"
    tools_allowlist: ["web_search"]
    budget_max: 1000
    depends_on: ["analyze"]

edges:
  - from: "search"
    to: "analyze"
  - from: "analyze"
    to: "synthesize"

metadata:
  category: "research"
  author: "your-team"
2

Register Template

Initialize registry with your template directory:
// In orchestrator initialization
registry := templates.InitTemplateRegistry(
    "./templates/builtin",
    "./templates/custom",  // Your custom templates
)

// Validate all templates
if err := registry.Finalize(); err != nil {
    log.Fatal(err)
}
3

Use Template

Via gRPC API:
grpcurl -plaintext -d '{
  "query": "Perform research on AI trends",
  "context": {
    "template": "research_workflow",
    "template_version": "1.0.0",
    "disable_ai": true
  }
}' localhost:50052 shannon.orchestrator.OrchestratorService/SubmitTask
Via HTTP Gateway:
curl -X POST http://localhost:8080/api/v1/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Perform research on AI trends",
    "context": {
      "template": "research_workflow",
      "template_version": "1.0.0"
    }
  }'
4

List Available Templates

# Via gRPC
grpcurl -plaintext -d '{}' localhost:50052 \
  shannon.orchestrator.OrchestratorService/ListTemplates
Note: HTTP Gateway template listing endpoint may not be implemented yet. Use gRPC for template discovery.

Template Best Practices

# Base template (base.yaml)
name: "base_research"
version: "1.0.0"
defaults:
  model_tier: "medium"
  budget_agent_max: 5000
  require_approval: false

# Child template (advanced_research.yaml)
name: "advanced_research"
version: "1.0.0"
extends: ["base_research"]  # Inherits all defaults
defaults:
  budget_agent_max: 10000  # Override specific values
registry := templates.InitTemplateRegistry("./templates")

// Validates:
// - YAML syntax
// - Required fields
// - Parameter types
// - Tool references
if err := registry.Finalize(); err != nil {
    log.Fatalf("Template validation failed: %v", err)
}
# Explicitly list allowed tools for security
tools_allowlist:
  - "web_search"
  - "calculator"
  - "python_executor"

# Don't use:
# tools_allowlist: ["*"]  # Too permissive

Add Tools Safely

For extending Shannon’s capabilities Shannon supports three tool integration methods:

MCP Tools

External HTTP APIs with zero code changes

OpenAPI Tools

Auto-generated from OpenAPI specs

Built-in Tools

Python tools for complex logic

Security Considerations

Always use tools_allowlist in templates to restrict which tools can be used.
Good:
# Template with restricted tools
tools_allowlist:
  - "web_search"
  - "calculator"
  - "my_custom_tool"
Bad:
# Don't allow all tools
tools_allowlist: ["*"]

Keep Experimental Tools Behind Flags

# config/features.yaml
experimental_tools:
  enabled: false  # Disabled by default

custom_analytics:
  enabled: ${ENABLE_CUSTOM_ANALYTICS}  # Env-based toggle
# In tool registration
if config.get("experimental_tools.enabled"):
    registry.register(ExperimentalTool)

Complete Tools Guide

See the complete guide for adding MCP, OpenAPI, and built-in Python tools

Vendor Extensions

For domain-specific agents and API integrations The vendor adapter pattern allows you to integrate proprietary APIs and specialized agents without modifying Shannon’s core code.

Architecture

Generic Shannon (Open Source)
├── python/llm-service/llm_service/tools/openapi_tool.py  # Generic loader
├── python/llm-service/llm_service/roles/presets.py       # Generic roles
├── go/orchestrator/internal/activities/agent.go          # Generic mirroring
└── config/shannon.yaml                                    # Base config

Vendor Extensions (Private)
├── config/overlays/shannon.vendor.yaml                    # Vendor configs
├── config/openapi_specs/vendor_api.yaml                   # API specs
├── python/llm-service/llm_service/tools/vendor_adapters/  # Transformations
│   ├── __init__.py                                        # Registry
│   └── vendor.py                                          # VendorAdapter
└── python/llm-service/llm_service/roles/vendor/           # Agent roles
    ├── __init__.py
    └── custom_agent.py                                    # System prompts

When to Use Vendor Extensions

Use when you need:
  • Domain-specific API integrations (analytics, CRM, e-commerce)
  • Custom field name transformations
  • Specialized agent roles with domain knowledge
  • Session context injection (account IDs, tenant IDs)
  • Private/proprietary tool configurations

Quick Start

1

Create Vendor Adapter

# python/llm-service/llm_service/tools/vendor_adapters/myvendor.py
class MyVendorAdapter:
    def transform_body(self, body, operation_id, prompt_params):
        # Field aliasing
        if "metrics" in body:
            body["metrics"] = [m.replace("users", "mv:users") for m in body["metrics"]]

        # Inject session context
        if prompt_params:
            body.update(prompt_params)

        return body
2

Register Adapter

# python/llm-service/llm_service/tools/vendor_adapters/__init__.py
def get_vendor_adapter(name: str):
    if name.lower() == "myvendor":
        from .myvendor import MyVendorAdapter
        return MyVendorAdapter()
    return None
3

Create Config Overlay

# config/overlays/shannon.myvendor.yaml
openapi_tools:
  myvendor_api:
    enabled: true
    spec_url: file:///app/config/openapi_specs/myvendor_api.yaml
    auth_type: bearer
    auth_config:
      vendor: myvendor  # Triggers adapter loading
      token: "${MYVENDOR_API_TOKEN}"
    category: custom
4

(Optional) Create Specialized Agent

# python/llm-service/llm_service/roles/myvendor/custom_agent.py
CUSTOM_AGENT_PRESET = {
    "name": "myvendor_agent",
    "system_prompt": "You are a specialized agent for...",
    "allowed_tools": ["myvendor_query", "myvendor_analyze"],
    "temperature": 0.7,
}
Register with graceful fallback:
# roles/presets.py
try:
    from .myvendor.custom_agent import CUSTOM_AGENT_PRESET
    _PRESETS["myvendor_agent"] = CUSTOM_AGENT_PRESET
except ImportError:
    pass  # Shannon works without vendor module
5

Use via Environment

SHANNON_CONFIG_PATH=config/overlays/shannon.myvendor.yaml
MYVENDOR_API_TOKEN=your_token_here

Benefits

  • Zero Shannon core changes - All vendor logic isolated
  • Clean separation - Generic infrastructure vs. vendor-specific
  • Conditional loading - Graceful fallback if vendor module unavailable
  • Easy to maintain - Vendor code in separate directories
  • Testable in isolation - Unit test adapters independently

Complete Vendor Adapters Guide

Comprehensive guide with examples, testing strategies, and best practices

Human Approval

For gating sensitive operations Wire require_approval through the SubmitTask request for human-in-the-loop control.

Configuration

# config/features.yaml
approvals:
  enabled: true
  dangerous_tools:
    - "file_write"
    - "code_execution"
    - "database_query"
  complexity_threshold: 0.7  # Require approval for complex tasks
  timeout_seconds: 7200      # 2 hour approval window

API Usage

curl -X POST http://localhost:8080/api/v1/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Delete all records from users table",
    "require_approval": true
  }'

Approval Flow

  1. Task submitted with require_approval: true
  2. Orchestrator pauses before execution
  3. Approval request sent via webhook/UI
  4. User approves/rejects via API
  5. Workflow continues or terminates
Gateway endpoint (recommended):
curl -X POST "http://localhost:8080/api/v1/approvals/decision"   -H "X-API-Key: YOUR_API_KEY"   -H "Content-Type: application/json"   -d '{
    "workflow_id": "<workflow-id>",
    "approval_id": "<approval-id>",
    "approved": true,
    "feedback": "Approved for execution"
  }'
Legacy admin endpoint at http://localhost:8081/approvals/decision is deprecated. Use the gateway endpoint instead.
Approval gates are enforced in the router before execution.

Feature Flags & Config

Runtime configuration without code changes Many behaviors are controlled via config/features.yaml and environment variables, loaded through GetWorkflowConfig.

Common Feature Flags

# config/features.yaml

# Template Fallback
template_fallback:
  enabled: true  # Fallback to AI if template fails

# Tool Selection
tool_selection:
  enabled: true  # Auto-select tools based on task
  max_tools: 5   # Maximum tools per task

# Cognitive Patterns
patterns:
  cot_enabled: true   # Chain-of-Thought
  tot_enabled: true   # Tree-of-Thoughts
  react_enabled: true # ReAct

# Budget Controls
budgets:
  enforce_token_limits: true
  enforce_cost_limits: true
  default_max_tokens: 10000
  default_max_cost_usd: 0.50

# Streaming
streaming:
  enabled: true
  buffer_size: 1000
  chunk_size: 512

Environment Variable Override

# Override via environment
TEMPLATE_FALLBACK_ENABLED=1
TOOL_SELECTION_MAX_TOOLS=10
DEFAULT_MAX_TOKENS=20000

# Start services with overrides
docker compose up -d

Dynamic Config Loading

// In orchestrator
config := GetWorkflowConfig()

if config.TemplateFallbackEnabled {
    // Use AI fallback
}

if config.BudgetsEnforceTokenLimits {
    // Apply token budget
}

Synthesis Templates (Output Customization)

For customizing how Shannon formats final research answers Synthesis templates control how multi-agent research results are formatted and presented. They’re particularly useful for Deep Research workflows.

When to Use

  • Customize output format for specific domains (market research, academic, executive summaries)
  • Enforce citation styles
  • Control answer structure and length
  • Inject domain-specific formatting rules

Template Methods

MethodContext ParameterUse Case
Named templatesynthesis_template: "name"Reusable .tmpl files
Verbatim overridesynthesis_template_override: "..."One-time custom format
Min lengthsynthesis_min_length: NEnforce minimum character count

Using Named Templates

Create templates in config/templates/synthesis/:
{{/* config/templates/synthesis/market_research.tmpl */}}
{{template "_base.tmpl"}}

## Executive Summary
Provide a 2-3 sentence summary of key findings.

## Market Analysis
- Key trends
- Competitive landscape
- Opportunities and risks

## Data Sources
Use [n] citation format for all claims.

## Recommendations
Actionable insights based on the analysis.
Use via API:
curl -X POST http://localhost:8080/api/v1/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Analyze the electric vehicle market in 2025",
    "context": {
      "force_research": true,
      "synthesis_template": "market_research"
    }
  }'

Verbatim Override

For one-time custom formatting without creating a template file:
curl -X POST http://localhost:8080/api/v1/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Compare major cloud providers",
    "context": {
      "force_research": true,
      "synthesis_template_override": "You are a cloud infrastructure analyst.\n\n## Analysis Format\n- Start with a comparison table\n- Use [n] citations\n- Include cost analysis section\n- End with recommendations"
    }
  }'
When using synthesis_template_override, you bypass the base template’s citation contract. You must include citation rules ([n] format) in your override text.

Minimum Length Control

Enforce a minimum output length:
curl -X POST http://localhost:8080/api/v1/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Comprehensive analysis of AI governance",
    "context": {
      "force_research": true,
      "synthesis_min_length": 5000
    }
  }'

Template Selection Logic

Selection is based on context and workflow signals:
  1. If context.synthesis_template is set → use that named template.
  2. Else if any of:
    • context.workflow_type == "research"
    • context.force_research == true
    • context.synthesis_style == "comprehensive"
    • context.research_areas is non-empty → use research_comprehensive.tmpl.
  3. Else if context.synthesis_style == "concise" → use research_concise.tmpl.
  4. Otherwise → use normal_default.tmpl.

Available Templates

TemplateDescription
_base.tmplProtected base with citation rules
normal_default.tmplDefault non-research synthesis (minimal structure)
research_comprehensive.tmplDetailed research format with strong coverage requirements
research_concise.tmplShorter research synthesis with lighter word requirements
research_with_facts.tmplResearch format optimized for fact extraction metadata
test_bullet_summary.tmplExample bullet-point summary template

Best Practices

  1. Always extend _base.tmpl - Ensures citation contract is maintained
  2. Use named templates for recurring formats
  3. Use override for one-off customizations
  4. Test templates with sample queries before production use

Template Directory

Templates are located in config/templates/synthesis/. See README.md in that directory for template authoring guidelines.

Best Practices Summary

  • Generic infrastructure: Committed to open source
  • Vendor-specific code: Kept private in separate directories
  • Configuration overlays: Domain-specific settings isolated
  • Conditional imports: Graceful fallback for optional modules
  • Use stable interfaces (ToolRegistry, TemplateRegistry, etc.)
  • Avoid forking core subsystems
  • Keep customizations in separate directories
  • Use feature flags for experimental changes
  • Allowlist tools in templates
  • Enable approvals for dangerous operations
  • Use domain allowlisting for external APIs
  • Keep secrets in environment variables
  • Unit test vendor adapters in isolation
  • Integration test with Shannon services
  • Use replay testing for workflow determinism
  • Validate templates with registry.Finalize()

Extension Decision Tree

Next Steps