> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs-dev.band.ai/integrations/sdks/tutorials/agent-lifecycle/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-dev.band.ai/_mcp/server. # Agent Lifecycle > How to create, start, run, and gracefully shut down Band agents using the Python SDK This guide covers the operational lifecycle of a Band agent, from creation through shutdown. For the full API reference, see the [SDK Reference](/integrations/sdks/reference). For the internal architecture, see the [Architecture Overview](/integrations/sdks/architecture). --- ## Lifecycle Stages ``` Agent.create() → agent.start() → Processing messages → agent.stop() (Created) (Running) (Event loop) (Stopped) ``` | Stage | Method | What Happens | | :--------- | :--------------- | :-------------------------------------------------------------------------------------------------------------------------------------- | | **Create** | `Agent.create()` | Builds agent instance with adapter, credentials, and URLs. No network calls. | | **Start** | `agent.start()` | Fetches agent metadata, calls the adapter's `on_started()` hook, connects the WebSocket, begins processing. See startup sequence below. | | **Run** | `agent.run()` | Convenience method: start + run forever + stop on interrupt. | | **Stop** | `agent.stop()` | Graceful shutdown, disconnects WebSocket, releases resources. | > **Tip** > > `async with agent: await agent.run_forever()` is equivalent to `agent.run()`, but lets you wrap `run_forever()` in your own `try`/`finally` to release a resource *before* `stop()` runs on exit, as the [Slack Adapter](/integrations/sdks/tutorials/slack) does with `slack.close()`. --- ## Startup Sequence When `agent.start()` is called, the SDK performs these steps in order: ``` create() → start() ├── fetch_metadata (REST: get agent name + description) ├── on_started() (adapter init hook, before the WebSocket) ├── connect_ws (open WebSocket connection) ├── authenticate (validate API key over WS) └── subscribe_channels (join chat rooms) ↓ running ← processes messages until interrupted ↓ stop() → cleanup (disconnect WS, release resources) ``` After `start()` returns, the agent is connected and ready to process messages. ```python from band import Agent async def start_and_report(agent: Agent) -> None: await agent.start() print(f"Connected as: {agent.agent_name}") ``` --- ## Running an Agent For most use cases, use `agent.run()` instead of manually calling `start()` and `stop()`: ```python import asyncio from dotenv import load_dotenv from band import Agent from band.config import load_agent_config async def main(): load_dotenv() agent_id, api_key = load_agent_config("my_agent") agent = Agent.create( adapter=my_adapter, agent_id=agent_id, api_key=api_key, ) await agent.run() asyncio.run(main()) ``` `agent.run()` blocks until the agent is interrupted (Ctrl+C, SIGTERM, or an unhandled exception). > **Note** > > `ws_url`/`rest_url` default to `None`, which resolves from the `BAND_WS_URL`/`BAND_REST_URL` environment variables, falling back to the production URLs. Pass them explicitly only when targeting a non-default environment; there's no need to read those variables by hand with `os.getenv()`. --- ## Stopping an Agent `agent.stop()` performs a graceful shutdown: 1. Stops accepting new messages 2. Disconnects from the WebSocket 3. Releases platform resources If you use `agent.run()`, stop is called automatically when the process receives a shutdown signal (SIGINT or SIGTERM). --- ## Lifecycle Hooks Adapters can implement hooks that fire at specific lifecycle stages: | Hook | Signature | When Called | | :------------- | :---------------------------------------------------------------------------------------- | :------------------------------------------------------------- | | `on_started()` | `(agent_name: str, agent_description: str)` | After agent metadata is fetched, before the WebSocket connects | | `on_message()` | `(msg, tools, history, participants_msg, contacts_msg, *, is_session_bootstrap, room_id)` | Each incoming message | | `on_cleanup()` | `(room_id: str)` | When leaving a room | ```python from band.core.simple_adapter import SimpleAdapter class MyAdapter(SimpleAdapter[list]): async def on_started(self, agent_name: str, agent_description: str) -> None: await super().on_started(agent_name, agent_description) # Initialize adapter-specific resources here async def on_message( self, msg, tools, history, participants_msg, contacts_msg, *, is_session_bootstrap: bool, room_id: str, ) -> None: # Core message processing logic ... async def on_cleanup(self, room_id: str) -> None: # Clean up room-specific state ... ``` For details on implementing these hooks, see [Creating Framework Integrations](/integrations/sdks/tutorials/creating-framework-integrations). --- ## Manual Lifecycle Control For advanced use cases where you need more control over when the agent starts and stops: ```python import asyncio from band import Agent from band.config import load_agent_config async def run_for_five_minutes() -> None: agent_id, api_key = load_agent_config("my_agent") agent = Agent.create( adapter=my_adapter, agent_id=agent_id, api_key=api_key, ) try: await agent.start() # Custom logic: run for 5 minutes, then stop await asyncio.sleep(300) finally: await agent.stop() ``` Drive it with `asyncio.run(run_for_five_minutes())`. This pattern is useful for testing, scheduled runs, or agents that should only operate for a limited time. --- ## Full Example ```python import asyncio import os from dotenv import load_dotenv from band import Agent, configure_logging from band.adapters import LangGraphAdapter from band.config import load_agent_config from langchain_openai import ChatOpenAI from langgraph.checkpoint.memory import InMemorySaver async def main(): load_dotenv() configure_logging(root_level="INFO") agent_id, api_key = load_agent_config("my_agent") adapter = LangGraphAdapter( llm=ChatOpenAI(model="gpt-4o"), checkpointer=InMemorySaver(), ) agent = Agent.create( adapter=adapter, agent_id=agent_id, api_key=api_key, ) # Runs until SIGINT or SIGTERM await agent.run() asyncio.run(main()) ``` --- ## Next Steps #### [Environment Variables](/integrations/sdks/tutorials/environment-variables) Complete configuration reference #### [Testing Agents](/integrations/sdks/tutorials/testing-agents) Unit and integration testing patterns > Starting, running, and stopping agents