Building Effect Machines
Esta página aún no está disponible en tu idioma.
In mashin, all external interaction is expressed as an intent. The runtime mediates each intent before it becomes an action. Effect machines are the services that fulfill those intents. The standard library provides common effects like HTTP requests and file operations. But when you need something specific, such as calling a proprietary API, running a Python ML model, or interfacing with a hardware device, you build an external effect machine.
An external effect machine is a small HTTP service that speaks mashin’s three-endpoint contract. You can write it in any language. mashin calls it like any other effect, with full governance: permission checks, audit logging, and budget enforcement.
The mental model
Your machine (.mashin) Your effect service (any language)┌──────────────────────┐ ┌──────────────────────┐│ │ HTTP POST │ ││ ask analyze, from: │ ───────────────► │ POST /execute ││ "my-ml-service" │ │ Your Python/Go/etc ││ │ ◄─────────────── │ logic runs here ││ (governed, audited) │ JSON response │ │└──────────────────────┘ └──────────────────────┘Your .mashin file calls the effect with ask ... from:. The runtime checks permissions, calls your service’s /execute endpoint, records the result in the behavioral ledger, and returns the output to the next step.
Step 1: Scaffold the effect service
Use the CLI to generate a starting point:
mashin effect init python my_analyzerThis creates a directory with a working effect service you can edit. Supported languages: python, typescript, go, rust, swift, elixir.
Or start from scratch using one of the SDKs.
Step 2: Implement your logic
Every effect service has two responsibilities:
execute: The actual work. Receives input, returns output.governance: Declares what the service does, what it needs, and what it returns.
Here is a Python example that wraps a sentiment analysis API:
from mashin_effects import EffectService
class SentimentEffect(EffectService): name = "sentiment" version = "1.0.0" capabilities = ["external_api_call"] inputs = [ {"name": "text", "type": "string", "required": True}, {"name": "language", "type": "string", "required": False} ] outputs = [ {"name": "sentiment", "type": "string"}, {"name": "confidence", "type": "number"} ]
def execute(self, input, context): text = input["text"] lang = input.get("language", "en")
# Call your internal API, ML model, database, etc. result = self._analyze(text, lang)
return { "sentiment": result["label"], "confidence": result["score"] }
def _analyze(self, text, lang): # Your actual logic here return {"label": "positive", "score": 0.92}
if __name__ == "__main__": SentimentEffect().serve(port=8080)The key design points:
- Capabilities declare what kind of effect this is. Common values:
external_api_call,file_system,database,compute,network. - Inputs and outputs declare the schema. mashin uses these for validation and documentation.
- The service runs as a standalone HTTP server. mashin does not embed your code; it calls your service over HTTP.
Step 3: Start and test the service
Start the service:
python my_analyzer/server.py# Or: mashin effect start my_analyzer/server.pyTest it:
# Quick test via CLImashin effect test http://localhost:8080
# Or manuallycurl http://localhost:8080/governancecurl -X POST http://localhost:8080/execute \ -H "Content-Type: application/json" \ -d '{"input": {"text": "This is wonderful"}, "context": {}}'The /governance endpoint should return your declaration. The /execute endpoint should return structured output.
Step 4: Register the effect service
Tell your cell about the service:
mashin effect register http://localhost:8080This reads the /governance endpoint and registers the service in your cell’s effect registry. Now machines can call it by name.
Step 5: Call it from a machine
machine analyze_feedback
accepts feedback as text, is required
responds with sentiment as text confidence as number
ensures permissions allowed to machine.call
implements ask result, from: "sentiment" text: input.feedback returns sentiment as text confidence as number assuming sentiment: "positive" confidence: 0.92The from: "sentiment" matches the name in your governance declaration. The assuming block provides mock values for testing.
Run it:
# With mock values (no API key needed)mashin test analyze_feedback.mashin
# Live execution (calls your running service)mashin run analyze_feedback.mashin --input '{"feedback": "Great product!"}'The governance declaration
The governance declaration is what makes external effects governable. When your service responds to GET /governance, it tells mashin:
{ "name": "sentiment", "version": "1.0.0", "capabilities": ["external_api_call"], "inputs": [ {"name": "text", "type": "string", "required": true} ], "outputs": [ {"name": "sentiment", "type": "string"}, {"name": "confidence", "type": "number"} ]}mashin uses this to:
- Check permissions: The calling machine must have
machine.callpermission and any capability-specific permissions - Validate inputs: The runtime checks that the machine sends the right fields
- Record in the ledger: The behavioral ledger records what was called, with what inputs, and what came back
- Generate documentation:
mashin infoand koda use the declaration to describe the effect
Real-world patterns
Wrapping an internal API
class CRMEffect(EffectService): name = "crm-lookup" version = "1.0.0" capabilities = ["external_api_call"] inputs = [{"name": "customer_id", "type": "string", "required": True}] outputs = [{"name": "name", "type": "string"}, {"name": "tier", "type": "string"}]
def execute(self, input, context): resp = requests.get(f"https://internal.crm.company.com/customers/{input['customer_id']}") data = resp.json() return {"name": data["full_name"], "tier": data["support_tier"]}Running a Python ML model
class ClassifierEffect(EffectService): name = "document-classifier" version = "2.0.0" capabilities = ["compute"] inputs = [{"name": "text", "type": "string", "required": True}] outputs = [{"name": "category", "type": "string"}, {"name": "probabilities", "type": "map"}]
def __init__(self): self.model = load_model("./classifier.pkl")
def execute(self, input, context): prediction = self.model.predict(input["text"]) return {"category": prediction.label, "probabilities": prediction.probs}Hardware or device integration (Go)
type GPIOEffect struct{}
func (g GPIOEffect) Execute(input, context map[string]any) (map[string]any, error) { pin := int(input["pin"].(float64)) state := input["state"].(string) err := gpio.Set(pin, state == "high") if err != nil { return nil, err } return map[string]any{"pin": pin, "state": state, "ok": true}, nil}Deployment
Effect services are standalone processes. Deploy them however you deploy any service:
- Local development:
mashin effect startruns the script directly - Docker: Package as a container, register the container URL
- Cloud: Deploy to any cloud platform, register the public URL
- Same machine as cell: Run alongside the cell process
The only requirement is that the cell can reach the service over HTTP.
Next steps
- Effect SDKs - SDK reference for all six languages
- Effects guide - Calling effects from machines
- CLI Effect Commands - Managing effect services