Complete reference for all Syrin features. Use this as a lookup when building agents.
Agent vs standalone: See Architecture to understand which components require an Agent and which work independently.
The main class for creating AI agents.
import os
from syrin import Agent, Model
class MyAgent(Agent):
# model = Model.OpenAI("gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY"))
model = Model.Almock() # No API Key needed
system_prompt = "You are helpful"
tools = [] # Optional tools
def __init__(self):
super().__init__()
# Optional: add memory, budget, etc.Key Methods:
agent.response(prompt)- Get a responseagent.astream(prompt)- Stream response piece by pieceagent.aresponse(prompt)- Async get response
See Models Guide for the complete documentation:
- Built-in models (OpenAI, Anthropic, Google, Ollama, LiteLLM)
- Model.Custom for third-party OpenAI-compatible APIs
- Custom models via inheritance and
make_model() - Tweakable properties (temperature, max_tokens, context_window, etc.)
- Fallbacks, structured output, centralized definitions
Quick reference:
import os
from syrin import Model
Model.OpenAI("gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY"))
Model.Anthropic("claude-sonnet", api_key=os.getenv("ANTHROPIC_API_KEY"))
Model.Custom("deepseek-chat", api_base="https://api.deepseek.com/v1", api_key="...")Routing: Pass model=[M1, M2, M3] + model_router=RoutingConfig(routing_mode=...) for per-request model selection. See Routing.
Give agents the ability to do things.
from syrin import tool
@tool
def my_tool(param1: str, param2: int = 10) -> dict:
"""Tool description for AI."""
return {"result": "value"}
class MyAgent(Agent):
tools = [my_tool]Group tools in MCP, use MCP in agents: Define an MCP with @tool, add to tools=[ProductMCP()]. See MCP.
Supported Types:
str- Textint- Whole numbersfloat- Decimalsbool- True/Falselist- Arraysdict- ObjectsOptional[Type]- May be None
Control costs (USD) and prevent overspending. Budget is spend only; for token usage caps use TokenLimits separately — see Budget Control.
import os
from syrin import Agent, Budget, RateLimit, Model, raise_on_exceeded
from syrin.threshold import BudgetThreshold
class BudgetAgent(Agent):
def __init__(self):
super().__init__()
self.budget = Budget(
run=0.10, # Max $0.10 per request
per=RateLimit(hour=5.00, day=50.00, month=500.00),
on_exceeded=raise_on_exceeded, # or warn_on_exceeded
thresholds=[
BudgetThreshold(at=80, action=lambda ctx: print(f"Budget at {ctx.percentage}%")),
BudgetThreshold(at=95, action=lambda ctx: ctx.parent.switch_model(Model.OpenAI("gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY")))),
]
)on_exceeded: Pass a callback. Use raise_on_exceeded to stop and raise, or warn_on_exceeded to log and continue.
Make agents remember things.
from syrin import Agent, Memory
class MemoryAgent(Agent):
def __init__(self):
super().__init__()
self.memory = Memory()Memory Types:
CORE- User identity/preferencesEPISODIC- Specific eventsSEMANTIC- General factsPROCEDURAL- Learned behaviors
Decay Strategies:
EXPONENTIAL- Old memories fade fastLINEAR- Fade at constant rateLOGARITHMIC- Fade slowlySTEP- Disappear suddenlyNONE- Never fade
Load documents from files, URLs, or raw text and attach them to an agent as a searchable knowledge base. Documents are normalized to a common Document type with content, source, source_type, and optional metadata.
Document model: Immutable. Required: content, source, source_type. Optional: metadata (e.g. page, section).
Source constructors (via Knowledge.*):
- Files:
Knowledge.PDF(path),Knowledge.Markdown(path),Knowledge.TextFile(path),Knowledge.YAML(path),Knowledge.JSON(path, jq_path=...),Knowledge.Python(path) - Directories:
Knowledge.Directory(path, glob="**/*.md", pattern=..., recursive=True) - Raw text:
Knowledge.Text("inline fact"),Knowledge.Texts(["fact1", "fact2"]) - Remote:
Knowledge.URL(url),Knowledge.GitHub(username, repos=["repo1", "repo2"])(orrepos=Nonefor all public repos)
Knowledge class (orchestrator): Full pipeline: load → chunk → embed → store → search. Requires embedding (e.g. Embedding.OpenAI). Lazy ingest on first search().
from syrin import Knowledge, KnowledgeBackend
from syrin.embedding import Embedding
from syrin.knowledge import Document
# Document: immutable, with source and source_type
doc = Document(content="Hello", source="user_provided", source_type="text")
# Build sources (loaders)
sources = [
Knowledge.PDF("./resume.pdf"),
Knowledge.Markdown("./about.md"),
Knowledge.Text("I have 5 years of Python experience."),
Knowledge.GitHub("myorg", repos=["project-a", "project-b"]),
]
# Knowledge orchestrator (embedding required)
knowledge = Knowledge(
sources=sources,
embedding=Embedding.OpenAI("text-embedding-3-small"),
backend=KnowledgeBackend.MEMORY, # or POSTGRES, SQLITE, etc.
)
# Attach to Agent for auto search_knowledge tool
# agent = Agent(model=..., knowledge=knowledge)
# await knowledge.ingest() # or lazy on first search
# results = await knowledge.search("Python experience")Agentic RAG: Set agentic=True to add search_knowledge_deep (multi-step retrieval) and verify_knowledge (claim verification) tools. The agent gets control over query decomposition, result grading, and iterative refinement.
from syrin.embedding import Embedding
from syrin.knowledge import AgenticRAGConfig, Knowledge
knowledge = Knowledge(
sources=[Knowledge.PDF("./resume.pdf"), Knowledge.Text("Fact.")],
embedding=Embedding.OpenAI("text-embedding-3-small"),
backend=KnowledgeBackend.MEMORY,
agentic=True,
agentic_config=AgenticRAGConfig(
max_search_iterations=3,
decompose_complex=True,
grade_results=True,
relevance_threshold=0.5,
search_model=None, # None = use agent's model
),
)
# Agent gets: search_knowledge, search_knowledge_deep, verify_knowledgeGrounding (anti-hallucination): Set grounding=GroundingConfig(...) for fact extraction, verification, and citations. Search returns pre-extracted, pre-verified facts instead of raw chunks.
from syrin.model import Model
from syrin.knowledge import GroundingConfig, Knowledge
knowledge = Knowledge(
sources=[...],
embedding=...,
agentic=True,
grounding=GroundingConfig(
enabled=True,
extract_facts=True,
cite_sources=True,
verify_before_use=True,
confidence_threshold=0.7,
max_chunk_preview=800, # max chars per chunk sent to fact extraction (default 800)
model=Model.OpenAI("gpt-4o-mini"), # separate cheaper model for grounding (optional)
),
)
# search_knowledge / search_knowledge_deep return verified facts with [Source: doc, Page N]
# Grounded facts are stored on the agent runtime and passed to output guardrails (e.g. FactVerificationGuardrail).
# response.report.grounding contains verified_count, total_facts, and sources when grounding was used.Hooks: KNOWLEDGE_AGENTIC_DECOMPOSE, KNOWLEDGE_AGENTIC_GRADE, KNOWLEDGE_AGENTIC_REFINE, KNOWLEDGE_AGENTIC_VERIFY, GROUNDING_EXTRACT_START, GROUNDING_EXTRACT_END, GROUNDING_VERIFY, GROUNDING_COMPLETE.
Knowledge Store (vector backends): Store chunks with embeddings for semantic search. Use get_knowledge_store(backend, ...) or instantiate directly.
- Backends:
KnowledgeBackend.MEMORY(testing, no deps),KnowledgeBackend.POSTGRES(pgvector, production),KnowledgeBackend.QDRANT,KnowledgeBackend.CHROMA,KnowledgeBackend.SQLITE(sqlite-vec) - Protocol:
KnowledgeStorehasupsert(chunks, embeddings),search(query_embedding, top_k, filter, score_threshold),delete(source=..., document_id=...),count() - Optional deps:
syrin[knowledge-postgres](asyncpg, pgvector),syrin[knowledge-sqlite](sqlite-vec). Qdrant/Chroma use existingsyrin[qdrant]/syrin[chroma].
from syrin import get_knowledge_store, KnowledgeBackend
from syrin.knowledge import Chunk
from syrin.knowledge.stores import InMemoryKnowledgeStore
# In-memory (no deps)
store = InMemoryKnowledgeStore(embedding_dimensions=1536)
# Or via factory
store = get_knowledge_store(KnowledgeBackend.MEMORY, embedding_dimensions=1536)
# Postgres: get_knowledge_store(KnowledgeBackend.POSTGRES, connection_url="...", embedding_dimensions=1536)Loaders: Each source has .load() (sync) and .aload() (async). GitHubLoader and URLLoader require async (.aload()). All return list[Document].
Chunking: Split documents into retrieval-optimized chunks with ChunkConfig, ChunkStrategy, and get_chunker(). Requires syrin[knowledge] (chonkie).
- Strategies:
RECURSIVE(default for general text),MARKDOWN(header-aware),PAGE(one chunk per page),CODE(AST-aware),SENTENCE,TOKEN,SEMANTIC(needsconfig.embedding),AUTO(selects bysource_type: code → CODE, markdown → MARKDOWN, pdf+has_pages → PAGE, else RECURSIVE). - Config:
ChunkConfig(strategy=..., chunk_size=512, chunk_overlap=0, min_chunk_size=50, ...). Useget_chunker(config)to get aChunker; callchunker.chunk(documents)orawait chunker.achunk(documents)for async (e.g. SemanticChunker). - Chunk: Each chunk has
content,metadata,document_id(= document source),chunk_index,token_count. Metadata includeschunk_strategyand, for markdown, optionalheading_hierarchy.
from syrin.knowledge import Document, ChunkConfig, ChunkStrategy, get_chunker
docs = [Document(content="Long text...", source="doc.txt", source_type="text")]
config = ChunkConfig(strategy=ChunkStrategy.RECURSIVE, chunk_size=256, min_chunk_size=0)
chunker = get_chunker(config)
chunks = chunker.chunk(docs)
# chunks[i].content, .document_id, .chunk_index, .token_count, .metadataSlot-based templates constrain LLM output to reduce hallucination. Use output_config on Agent; when a template is set, structured output fills slots and response.content is the rendered text. File generation (TEXT, MARKDOWN, HTML, PDF, DOCX) produces response.file and response.file_bytes. When citation is set, citations are parsed from content, styled (inline, footnote, appendix), and response.citations is populated.
from syrin import CitationConfig, CitationStyle, OutputConfig, OutputFormat, SlotConfig, Template
# Standalone
tpl = Template("cap", "Amount: {{amount}}", slots={"amount": SlotConfig("str")})
tpl.render(amount="₹50L")
# With Agent (requires output=Output(MyModel))
agent = Agent(
model=model,
output=Output(CapitalData),
output_config=OutputConfig(format=OutputFormat.TEXT, template=tpl),
)
response = agent.response("...")
# response.content = rendered template
# response.template_data = slot values
# response.file, response.file_bytes when output_config format produces file
# With citations (financial, legal, medical)
agent = Agent(
model=model,
output_config=OutputConfig(
format=OutputFormat.PDF,
citation=CitationConfig(style=CitationStyle.FOOTNOTE, include_page=True),
),
)
# LLM output: "Cap is ₹50L [Source: moa.pdf, Page 3]"
# response.content = content with [1], [2] footnotes + References section
# response.citations = [Citation(text="...", source="moa.pdf", page=3)]Syntax: Mustache-style — {{var}}, {{#section}}...{{/section}}, {{#list}}{{.}}{{/list}}. See Template Engine.
What you get back from agent.response():
response = agent.response("Hello")
response.content # The answer text
response.citations # Parsed citations when output_config.citation is set
response.cost # $ spent
response.tokens # Tokens used
response.model # Which model
response.duration # Seconds taken
response.stop_reason # Why it stopped
response.tool_calls # Tools used
response.budget_remaining # Budget left
response.budget_used # Budget used
response.raw # Raw API responseServe your agent via HTTP, CLI, or STDIO:
from syrin.enums import ServeProtocol
agent.serve(port=8000) # HTTP: POST /chat, /stream, GET /health, etc.
agent.serve(protocol=ServeProtocol.CLI) # CLI: terminal REPL
agent.serve(protocol=ServeProtocol.STDIO) # STDIO: JSON lines on stdin/stdout
agent.serve(port=8000, enable_playground=True) # Web playground at /playgroundSee Serving.
Group related tools in an MCP and add MCP to your agent's tools:
from syrin import MCP, Agent, tool
class ProductMCP(MCP):
@tool
def search_products(self, query: str) -> str:
"""Search products."""
return f"Results: {query}"
class ProductAgent(Agent):
tools = [ProductMCP()] # MCP tools become agent toolsWhen serving, /mcp is auto-mounted alongside /chat. See MCP.
Use these constants instead of strings:
from syrin import LoopStrategy
# For budgets
warn_on_exceeded
raise_on_exceeded
# For loops
LoopStrategy.REACT # Think-Act-Observe (default)
LoopStrategy.SINGLE_SHOT # Single response
LoopStrategy.PLAN_EXECUTE # Plan then execute
LoopStrategy.CODE_ACTION # Generate and execute codeSee Use Case 1: Simple Q&A Agent
See Use Case 2: Research Agent with Tools
See Use Case 3: Agent with Memory
See Use Case 4: Budget Control
See Use Case 5: Multi-Agent Orchestration
import os
from syrin import Agent, Model
class SimpleAgent(Agent):
# model = Model.OpenAI("gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY"))
model = Model.Almock() # No API Key needed
system_prompt = "Help users"
agent = SimpleAgent()
response = agent.response("Your question")
print(response.content)import os
from syrin import Agent, Model, tool
@tool
def do_something(param: str) -> dict:
"""Do something."""
return {"result": param.upper()}
class ToolAgent(Agent):
# model = Model.OpenAI("gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY"))
model = Model.Almock() # No API Key needed
tools = [do_something]
agent = ToolAgent()
response = agent.response("Use your tool")import os
from syrin import Agent, Memory, Model
class MemoryAgent(Agent):
# model = Model.OpenAI("gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY"))
model = Model.Almock() # No API Key needed
def __init__(self):
super().__init__()
self.memory = Memory()
agent = MemoryAgent()
agent.response("Remember: my name is Alice")
agent.response("What's my name?") # It remembers!import os
from syrin import Agent, Budget, Model, raise_on_exceeded
class BudgetAgent(Agent):
# model = Model.OpenAI("gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY"))
model = Model.Almock() # No API Key needed
def __init__(self):
super().__init__()
self.budget = Budget(
run=0.10,
on_exceeded=raise_on_exceeded
)
agent = BudgetAgent()
response = agent.response("Do something")
print(f"Cost: ${response.cost:.4f}")import os
from syrin import Agent, Model
class Agent1(Agent):
# model = Model.OpenAI("gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY"))
model = Model.Almock() # No API Key needed
system_prompt = "You are Agent 1"
class Agent2(Agent):
# model = Model.OpenAI("gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY"))
model = Model.Almock() # No API Key needed
system_prompt = "You are Agent 2"
a1 = Agent1()
a2 = Agent2()
r1 = a1.response("Question 1")
r2 = a2.response("Question 2")import os
from syrin import Agent, Model
class StreamAgent(Agent):
# model = Model.OpenAI("gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY"))
model = Model.Almock() # No API Key needed
agent = StreamAgent()
for chunk in agent.astream("Your prompt"):
print(chunk.text, end="", flush=True)"API key not found"
- Pass
api_keyexplicitly:Model.OpenAI("gpt-4o-mini", api_key=os.getenv("OPENAI_API_KEY")) - The library does not auto-read API keys from environment variables
"Model not found"
- Check model name spelling
- Make sure API credentials are valid
- Verify you have API credits
"Budget exceeded"
- Check your budget settings
- Use cheaper models (gpt-4o-mini, claude-3-haiku)
- Reduce the scope of requests
"Tool not working"
- Make sure tool returns a dict
- Check parameter types match usage
- Verify tool is in agent.tools list
-
Use right model
- Testing: Use gpt-4o-mini
- Production: Use gpt-4o or claude-3-sonnet
- Advanced: Use gpt-4 or claude-3-opus
-
Set budgets
- Always set per-request budget
- Add daily/monthly limits
-
Use tools wisely
- Keep tools simple
- Don't overload with tools
- Document tool purpose clearly
-
Stream for large responses
- Use
astream()for essays/stories - Use
run()for short responses
- Use
-
Cache prompts
- Reuse agent instances
- Don't recreate agents per request
Configuration overrides from a backend (Syrin Cloud or self-hosted) without code deploys. Call syrin.init(api_key=...) (or set SYRIN_API_KEY) to enable; agents then register and receive overrides. When serving, GET/PATCH /config and GET /config/stream are available. Types and wire format: Remote Config.
All examples are in /examples/ directory:
examples/phase1_basic_agent.py- Basic agentexamples/phase2_memory_system.py- Memoryexamples/phase3_multi_agent.py- Multi-agentexamples/phase4_budget_testing.py- Budgetexamples/phase5_async_streaming.py- Streamingexamples/advanced/- Advanced patterns
- Pick a Use Case Guide
- Try building your own agent
- Check examples in
/examples/ - Read Getting Started again if stuck
Need help? Check the FAQ