> ## Documentation Index
> Fetch the complete documentation index at: https://docs.collinear.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# BaseAgent

> The core contract for building custom agents

SimLab is agent-agnostic. The default reference agent uses a tool-calling loop with any LLM API (via LiteLLM), but you can bring your own agent by implementing the `BaseAgent` contract. Install via:

```bash theme={null}
pip install simulationlab
```

## Contract

Your agent extends `BaseAgent` and implements four methods:

```python theme={null}
from simlab.agents import BaseAgent, BaseEnvironment, RunArtifacts, ToolCall

class MyAgent(BaseAgent):
    @staticmethod
    def name() -> str:
        return "my-agent"

    def version(self) -> str | None:
        return "1.0.0"

    def setup(self, environment: BaseEnvironment) -> None:
        """Called once before run(). Use to discover tools, warm up models, etc."""
        self.tools = environment.list_tools()

    def run(
        self,
        instruction: str,
        environment: BaseEnvironment,
        context: RunArtifacts,
    ) -> None:
        """Execute the task using a think-act-observe loop."""
        # 1. Send the instruction to your LLM along with available tools
        messages = [{"role": "user", "content": instruction}]
        context.record_message("user", instruction)

        max_steps = context.max_steps or 20
        for step in range(max_steps):
            # 2. Think — ask your LLM to decide the next action
            llm_response = self.call_llm(messages, tools=self.tools)

            if llm_response.is_done:
                # LLM decided the task is complete
                context.set_final_observation(llm_response.text)
                break

            # 3. Act — execute the tool call the LLM chose
            tool_call = ToolCall(
                tool_server=llm_response.tool_server,
                tool_name=llm_response.tool_name,
                parameters=llm_response.parameters,
            )
            result = environment.call_tool(
                tool_call.tool_server,
                tool_call.tool_name,
                tool_call.parameters,
            )

            # 4. Observe — record the result and feed it back to the LLM
            context.record_tool_call(tool_call, result)
            messages.append({"role": "assistant", "content": f"Called {tool_call.tool_name}"})
            messages.append({"role": "tool", "content": str(result.observation)})
```

<Note>`self.call_llm()` is your own LLM integration — use any provider (OpenAI, Anthropic, etc.). SimLab is model-agnostic.</Note>

## Lifecycle

1. The runner calls `setup()` once, then `run()`.
2. If the agent exceeds the timeout, the run is terminated and artifacts captured up to that point are saved.

## Running Your Agent

Register your agent at runtime via the CLI:

```bash theme={null}
simlab tasks run --env my-env --task my-task --agent-import-path path.to.agent:MyAgent
```

If `--agent-import-path` is omitted, the CLI uses the built-in reference agent.
