Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Note: This document is also available in Spanish as README.es.md.

AI agent with LM Studio

A small, reusable local agent that calls the LM Studio HTTP API directly. It can hold conversations, switch profiles, and run tools without using the OpenAI SDK.

Requirements

  • Python 3.12 or later.
  • Poetry.
  • LM Studio with the server running and a model loaded.
  • An LM Studio API key if authentication is enabled.
  • Firecrawl only for profiles that use web search.

Configuration

Copy .env.example to src/agent/.env and fill in the private values. The .env file is ignored by Git and must not be included in commits.

LM_STUDIO_URL=http://localhost:1234/v1
LM_STUDIO_API_KEY=replace-with-your-key
LM_STUDIO_MODEL=replace-with-the-loaded-model-id

FIRECRAWL_URL=https://api.firecrawl.dev/v2/search
FIRECRAWL_API_KEY=replace-with-your-key

LM_STUDIO_URL normally uses HTTP for the local LM Studio server. Use HTTPS only when an actual TLS proxy is running in front of the server.

Firecrawl supports two explicit response contracts:

  • A URL containing /v1/ must return data as a list.
  • A URL containing /v2/ must return data.web as a list.

A response that does not match the version in its URL is treated as an error, not as an empty search.

Optional settings

Variable Default Purpose
DEFAULT_AGENT research Initial profile
LM_STUDIO_TIMEOUT 120 LM Studio timeout in seconds
FIRECRAWL_TIMEOUT 30 Firecrawl timeout in seconds
HTTP_RETRIES 2 Retries for network failures, 429, and 5xx errors
MAX_TOOL_ROUNDS 5 Maximum number of tool rounds
MAX_HISTORY_MESSAGES 40 Approximate conversation-history limit

Running the agent

The original command still works:

poetry run python src/agent/agent.py

You can also run the package:

PYTHONPATH=src poetry run python -m agent

Select an initial profile with:

poetry run python src/agent/agent.py --agent analyst

Interactive commands

/agents          list profiles
/agent NAME      switch profiles and start a new conversation
/new             clear the current conversation
/history         show a summary of the stored history
/help            show help
/quit            exit

You can also exit by entering exit, quit, q, or bye, in any combination of uppercase and lowercase letters. These commands are handled locally and do not call LM Studio.

Switching profiles resets the conversation so incompatible system instructions are not mixed together.

Included profiles

  • research: web research with required sources.
  • analyst: analysis that separates facts, assumptions, and conclusions.
  • general: local conversation without external tools.
  • writer: writing and rewriting with more creative variation.

Each profile defines only three things: a system prompt, tool names, and generation parameters. Profiles are stored in src/agent/profiles.py.

How a question is processed

  1. The CLI adds the question to the conversation history.
  2. The runner sends the history, profile, and tools to LM Studio.
  3. If the model requests tools, Python validates and executes each call.
  4. Structured results are added to the history with the tool role.
  5. LM Studio receives the updated history and decides whether to search again or respond.
  6. When the tool limit is reached, one final request is made without tools, guaranteeing an opportunity to synthesize the results.
  7. The CLI prints one response and keeps the most recent complete interactions.

The model never executes Python itself. It only requests tools through the protocol; the dispatcher decides which functions are allowed.

Project structure

src/agent/
├── agent.py         entry point compatible with the original script
├── __main__.py      entry point for `python -m agent`
├── cli.py           commands and interactive session
├── config.py        environment loading and validation
├── http_utils.py    JSON POST requests and bounded retries
├── lm_studio.py     LM Studio chat transport
├── profiles.py      profiles and generation parameters
├── runner.py        model-tool-model loop
└── tools.py         schemas, Firecrawl, and tool dispatcher

The implementation uses functions and dictionaries. The only objects with a lifecycle are the httpx clients, because they maintain reusable HTTP connections.

Adding a profile

Add an entry to AGENT_PROFILES in profiles.py:

"translator": {
    "description": "Translation without external tools",
    "system_prompt": "Translate faithfully and preserve formatting.",
    "tool_names": [],
    "generation": {
        "temperature": 0.1,
        "top_p": 0.9,
        "max_tokens": 1600,
    },
},

The runner and CLI do not need to be changed.

Adding a tool

A tool requires three changes in tools.py:

  1. Add its schema to TOOL_DEFINITIONS.
  2. Write a handler with simple arguments.
  3. Register the handler in TOOL_HANDLERS.

Then add its name to the profiles that are allowed to use it. An unknown name or invalid JSON produces a controlled error result for the model.

Errors and observability

Each turn displays:

  • the current loop phase;
  • response time;
  • finish_reason;
  • token usage when LM Studio includes it;
  • the approximate message and character count of the history;
  • the name, status, and result count for each tool.

Configuration errors appear before the session starts. Tool errors are returned to the model so it can explain them. The CLI rolls back a failed interaction so it does not affect the next question. API keys are never printed.

Tests

poetry run pytest -q

The tests do not access LM Studio or Firecrawl. They use local HTTP transports and cover v1/v2 contracts, authentication, retries, tools, final synthesis, invalid responses, and conversation-history trimming.

About

A simple AI agent that uses LM Studio models and Firecrawl to query data on Internet.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages