cas-studio

cas-studio

Enables AI hosts to design and simulate complex adaptive systems, including managing agent types, running simulations, and analyzing emergent behavior, feedback loops, and sensitivities.

Category
访问服务器

README

CAS Studio

A studio for designing and simulating complex adaptive systems (CAS) as agent-based models. You define agent types (state + local rules), wire agents into a directed, signed interaction graph, give the system an open environment with sources and sinks, and run a deterministic, seeded, synchronous simulation engine against it. The seven canonical CAS properties are first-class, instrumented features — each has explicit model constructs and analysis endpoints, not just documentation.

Pure Python + numpy core. FastAPI + SQLAlchemy + Alembic for the REST API and persistence. Vanilla-JS canvas UI. No heavy deps, no network calls, no external data.

Quickstart

./run.sh                      # venv + deps (via uv), alembic upgrade, uvicorn
# HOST=127.0.0.1 PORT=8002 ./run.sh   to override

Then open http://localhost:8000/ — on first start against an empty database the seed demo is loaded (and audit-logged): innovation diffusion in a market — 40 agents on a small-world ring lattice with innovators, early-majority and laggard adopters, two skeptics (balancing loops), two adaptive-price vendors, and an information source in the environment. Two adjacent seeded adopters trigger an S-curve adoption cascade (mean adoption 0.12 → 0.95 over ~11 steps) with flagged emergence events at the takeoff; dropping to a single seed makes it fizzle.

Run the tests:

.venv/bin/python -m pytest tests/ -q

The seven CAS properties

1. Emergence

Macro patterns arise from micro interactions. Every step the engine records macro metrics: the mean field of every state var, population variance, active-cluster count (connected components of "active" agents — primary var ≥ 0.5 — over the undirected interaction graph), an order parameter |2·active_fraction − 1|, and a Moran's-I-like neighbor correlation. GET /api/runs/{id}/emergence returns the series plus flagged emergence events: steps where the z-score of a macro indicator's step-to-step delta exceeds 3 while exogenous inputs were constant (no injections). Honest caveat: a z-score over a short series is a blunt detector — treat events as flags for inspection, not proof.

2. Nonlinearity

POST /api/systems/{id}/sensitivity with {"param", "deltas": [...], "steps", "seed"} reruns the simulation with param perturbed by each delta and reports response ratios |Δoutcome/Δparam| (outcome = final mean field of the primary var). It flags nonlinear (ratios vary by >10× across magnitudes — superlinear regime), threshold (some perturbations produce a response, others none), and sign_flip. Parameter addressing:

param form meaning
env:<var> environment initial value
flow:<var>:inflow / flow:<var>:outflow_rate source/sink field
type:<type>:<var> initial state var of every agent of a type
seed_count:<type>:<var> how many agents of a type start with <var>=1 (a contiguous block anchored at the current first active agent)

The seed demo has a genuine threshold at seed_count:innovator:adopted: one seed fizzles (adoption 0.075), two adjacent seeds cascade (0.95).

3. Decentralization

Agents act only on local information: their own state, weighted means of their direct in-neighbors' state, and environment variables via flux. The rule DSL's condition vocabulary is closed — global state is unrepresentable by construction (there is no "all agents" aggregate, no global lookup). Self-organization is tracked by the Moran's-I-like neighbor-state correlation in every run's series.

4. Feedback loops

GET /api/systems/{id}/loops enumerates elementary cycles of the directed interaction graph (Johnson-style DFS, each cycle reported once from its smallest node; max_len and a 1000-cycle cap bound the enumeration and report truncated). Each loop is classified reinforcing (even count of negative couplings) or balancing (odd count) with loop gain = product of edge weights. Run summaries include per-loop activity: the edge flux that flowed through each loop's edges during the run, where edge flux per step = |Δ primary var of the source| × |weight| (a heuristic for "how much change propagated along this edge", not a physical quantity).

5. Adaptation

Agent types may declare "adaptation": {"target_var", "target_value", "rate"}. Each step the agent adds a bounded bias to target_var; after the step the bias is updated by gradient-free reinforcement: if the step moved the var closer to target_value, keep and amplify the bias (×1.25, capped at |1.0|), else reverse and damp it (×−0.5). Deterministic given the seed. The seed demo's vendors hill-climb price toward 0.7 this way.

6. Open boundaries

Every system has an environment: named float variables plus sources/sinks — {"var": "information", "inflow": 1.0, "outflow_rate": 0.05} applies v += inflow − outflow_rate·v per step. Agents exchange with the environment through conserving flux effects: {"flux": "information", "by": 0.02} moves 0.02 units from the environment into the agent (a state var of the same name); the environment loses exactly what agents gain (verified by test). POST /api/runs/{id}/inject with {"step", "var", "amount"} schedules an exogenous pulse: it re-runs the base run's system/seed/steps with the pulse added to the environment at that step, and returns the new run plus the outcome delta.

7. Nested hierarchy

A System may have a parent_id; subsystems are full systems with their own agents, rules and runs. GET /api/systems/{id}/rollup aggregates each child system's latest-run macro metrics into the parent's report (agent-count-weighted mean of the children's primary mean fields).

Rule DSL

An agent type has state (dict of float vars) and rules — a list of {"if": <condition>, "then": [<effect>, ...]}. The first matching rule fires; later rules are ignored that step (a rule with "then": [] is an absorbing-state guard).

Conditions:

"always"
{"var": "adopted", "op": ">=", "value": 0.5}                 // own state
{"neighbor_mean": {"var": "adopted"}, "op": ">=", "value": 0.35}  // in-neighbors

op> < >= <= ==. neighbor_mean is the weighted mean Σ wᵢxᵢ / Σ|wᵢ| over in-neighbors that carry the var — so negative-weight (skeptic) neighbors dilute the mean.

Effects:

{"set": "adopted", "value": 1.0}        // set own var
{"adjust": "energy", "by": -0.1}        // add to own var
{"flux": "information", "by": 0.02}     // conserve quantity with the environment

Individual agents may carry a state_override: floats, or {"uniform": [lo, hi]} which is sampled once per run from the run's seeded RNG (np.random.default_rng(seed)) — this is what makes the run seed meaningful. Same model + same seed → identical metric series (tested).

The engine steps synchronously: every agent computes its next state from the same snapshot — no update-order artifacts (tested with an oscillating two-agent model).

REST API

All mutations are audit-logged (GET /api/audit).

Endpoint Purpose
GET/POST /api/systems, GET/DELETE /api/systems/{id} system CRUD (parent_id for hierarchy)
POST /api/systems/{id}/agent-types, DELETE /api/agent-types/{id} agent types (DSL validated on write)
POST /api/systems/{id}/agents, DELETE /api/agents/{id} agents
POST /api/systems/{id}/interactions, DELETE /api/interactions/{id} directed signed edges
GET/PUT /api/systems/{id}/environment environment variables + flows
PATCH /api/agents/{id}/position {pos_x, pos_y} persist an agent's canvas position (saved by the UI on drag-end)
POST /api/systems/{id}/reset_positions clear all saved canvas positions in a system (UI "Reset layout")
PUT /api/agents/{id} {name?, state_override?} edit agent (validated, audit-logged)
PUT /api/interactions/{id} {weight} edit edge weight (audit-logged)
PUT /api/agent-types/{id} edit type state/rules/adaptation (DSL-validated)
GET /api/examples, POST /api/examples/{name} curated example systems, one-click copies
POST /api/agent/chat, POST /api/agent/generateGET /api/agent/jobs/{id} LLM jobs: submit (202) then poll for progress/result
POST /api/systems/{id}/runs {steps, seed} run the sim → {run_id}
GET /api/runs/{id} summary (final mean field, loop activity, final states)
GET /api/runs/{id}/series per-step macro metrics
GET /api/runs/{id}/emergence emergence events
POST /api/runs/{id}/inject {step, var, amount} exogenous pulse → new run
GET /api/systems/{id}/loops feedback loops, classified
POST /api/systems/{id}/sensitivity perturbation response analysis
GET /api/systems/{id}/rollup aggregate children's latest runs

MCP integration

CAS Studio ships an MCP (Model Context Protocol) stdio server so AI hosts (Cursor, Claude Desktop, …) can design and simulate systems directly. It fronts the REST API over HTTP — point it at any running instance with CAS_API_URL (default http://127.0.0.1:8000).

Register it in your host's MCP config (see mcp-config.example.json):

{
  "mcpServers": {
    "cas-studio": {
      "command": "/path/to/cas-studio/.venv/bin/python",
      "args": ["-m", "app.mcp_server"],
      "cwd": "/path/to/cas-studio",
      "env": {"CAS_API_URL": "http://127.0.0.1:8000"}
    }
  }
}

Tools exposed (each proxies the matching REST endpoint above): list_systems, get_system, list_agent_types, create_agent_type, create_agents (batch), add_interaction, set_environment_flow, run_simulation, get_run_series, get_emergence_events, list_feedback_loops, run_sensitivity, inject_pulse, get_rollup, and describe_cas_properties (the seven-property guide + rule DSL).

AI agent

The UI's AI Agent tab is a built-in chat agent that drives the studio through a local Ollama LLM — no API keys, nothing leaves the machine.

Requirements: Ollama running (ollama serve) and at least one model, e.g.:

ollama pull qwen3:8b   # the default; supports tool calling and thinking

The model dropdown lists whatever Ollama reports installed and defaults to qwen3:8b, then any gemma3*, then llama3.1:8b, then the first model (GET /api/agent/models). Point the agent at another Ollama host with OLLAMA_URL (default http://127.0.0.1:11434).

POST /api/agent/chat and POST /api/agent/generate run as background jobs (HTTP 202 {job_id}) so slow local LLMs never hang a request: poll GET /api/agent/jobs/{id} for {status: pending|running|done|error, progress: [...], result|error} — progress entries appear as the loop runs ("round 2: calling run_simulation…", "attempt 2: validation failed: …") and every failure ends in a specific human-readable error. LLM calls use Ollama's native /api/chat with think: false (qwen3's thinking mode is ~10× slower on CPU-bound Ollama and adds nothing to tool calls) and the default round cap is 5.

POST /api/agent/chat {messages, model?, system_id?, temperature?, max_rounds?} runs a tool-calling loop against Ollama's /api/chat: the model picks a tool, the server executes it (through the same tool registry the MCP server uses, app/tools.py), appends the result, and repeats — up to max_rounds — until the model writes a final answer. The job result is {reply, model, tool_trace, rounds}; tool_trace lists every call with its arguments and a trimmed result, and the UI renders it as a collapsible block under each reply. Chain-of-thought (<think> blocks from qwen3) is stripped from replies. If the selected model doesn't support tool calling, the agent falls back to a single-shot no-tools answer with a note; if Ollama is down, the models endpoint degrades gracefully and jobs end with a clear error.

POST /api/agent/generate {description, name?, model?, system_id?, temperature?, max_rounds?} turns plain English into a running CAS model. The LLM (Ollama JSON mode) drafts a system spec — agent types with rule-DSL behavior, agent counts, an interaction topology (ring_lattice / random / small_world), signed edge weights by source type, environment variables with sources/sinks, and optional nested child systems — guided by a compact DSL reference, a few-shot example, and systems-engineering design rules (typology decomposition, at least one reinforcing and one balancing loop, open-boundary flows for conserved quantities, nesting for systems-of-systems). The server validates the spec hard (schema, the full rule-DSL validator, topology materialization); on failure the validation errors are fed back to the model, up to max_rounds attempts (default 3), after which the job ends in error with the collected messages. On success everything is created through the repository (audit-logged as generate_system by ai-agent) and the result carries the new system id, a summary, and the raw spec. The same capability is exposed to MCP hosts as the generate_system_from_description tool (it submits the job and waits), and in the UI as the Generate system mode of the AI Agent tab (with live progress).

LLM settings

The gear button in the AI Agent tab opens settings: temperature (default 0.2) and max tool rounds (default 8) — sent per request as temperature / max_rounds to both /api/agent/chat and /api/agent/generate — plus the active Ollama URL and ollama pull hints. Model, temperature and round choices persist in localStorage.

Editing, examples, and guided UX

The canvas has an edit toolbar (Select / Add agent / Connect / Delete) for manual model building — clicking a node or edge opens an editor in the detail panel (name, state override, edge weight). The Structure tab edits agent types (state schema + rule DSL with inline validation errors) and the environment (variables + source/sink flows). New endpoints: PUT /api/agents/{id}, PUT /api/interactions/{id}, PUT /api/agent-types/{id} (all validated and audit-logged). The Examples tab loads curated systems in one click (GET /api/examples, POST /api/examples/{name}): the innovation-market cascade, a predator–prey meadow, and a two-tier supply network (nested hierarchy) — each with a what-to-watch blurb. A first-run help overlay, per-tab explainer bars, and empty-state hints guide new users; the emergence panel shows the strongest sub-threshold shifts (near_misses on GET /api/runs/{id}/emergence) when no events fire.

Graph canvas

The system graph is fully interactive: drag empty space to pan, mouse-wheel to zoom (anchored at the cursor, with a zoom indicator and a Fit button), drag nodes to rearrange them — positions persist server-side (pos_x/pos_y, saved on drag-end) and auto-layout only fills nodes without a saved position. Edges show direction arrows, green solid / red dashed for positive / negative coupling, and weight labels where they carry information beyond the sign. Nodes are labeled with name

  • primary state value after a run; hovering shows a tooltip with the full agent state; a type legend sits in the corner. The pure geometry/layout helpers live in app/static/graph.js and are unit-tested with node --test (see tests/js/).

Layout

app/
  main.py        FastAPI app, REST API, static UI
  tools.py       shared tool registry (names/schemas/execution) for MCP + agent
  mcp_server.py  MCP stdio server fronting the REST API (CAS_API_URL)
  agent.py       in-app AI agent: Ollama tool-calling loop (OLLAMA_URL)
  jobs.py        in-process background jobs for slow LLM work (submit/poll)
  generate.py    natural-language -> validated CAS system spec -> repository
  examples.py    curated one-click example systems
  db.py          engine + session factory (DATABASE_URL, default sqlite:///./cas_studio.db)
  repository.py  SQLAlchemy models + Repository (single DB access point)
  rules.py       rule DSL validation + evaluation (pure functions)
  engine.py      deterministic seeded synchronous simulation engine
  analysis.py    emergence events, loops, sensitivity, Moran's I
  seed.py        the innovation-diffusion demo model
  static/index.html  canvas UI (graph, run controls, chart, loops/sensitivity/inject panels, AI agent)
  static/graph.js    pure canvas helpers (view transform, force layout, edge geometry)
alembic/         schema migrations
tests/           pytest, one file per concern

License

MIT — © 2026 Vector Stream Systems LLC.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选