Skip to main content
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:
pip install simulationlab

Contract

Your agent extends BaseAgent and implements four methods:
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)})
self.call_llm() is your own LLM integration — use any provider (OpenAI, Anthropic, etc.). SimLab is model-agnostic.

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:
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.