micro-agents

Small async building blocks for embedding an LLM tool-loop in a bot or service.

from micro_agents import Aggregator, OpenAIBasedLLM, Tool, ToolManager


def current_weather(city: str) -> str:
    """Return the current weather for a city."""
    return f"Sunny in {city}"


class BotTools(ToolManager):
    async def is_allowed(self, tool, context: dict[str, object]) -> bool:
        return context.get("role") == "member"


def master_prompt(context: dict[str, object]) -> str:
    return f"You are a concise support bot. Reply in {context.get('language', 'en')}."


llm = OpenAIBasedLLM(api_url="https://api.openai.com/v1", api_key="...", model="gpt-5")
agent = Aggregator(
    llm,
    BotTools([Tool(current_weather)]),
    master_prompt=master_prompt,
)
session = agent.create_session(context={"user_id": 42, "role": "member", "language": "ru"})

result = await session.run("Какая погода в Варшаве?")
if result.succeeded:
    print(result.final_message.message())

Use async for event in session.stream(message) when the embedding needs tool-progress or message events. A session retains its conversation and a live application-owned context dict. ToolManager.is_allowed() is checked when tools are shown to the model and again immediately before execution.

An individual session can override the aggregator's defaults:

session = agent.create_session(llm=another_llm, thinking=True)

If llm is omitted, the aggregator's LLM is used. thinking=False disables reasoning, thinking=True requests medium reasoning, and the default None leaves the provider's behavior unchanged.

Attach a local image, document, or MP3/WAV file with Media.from_path(). Images and files may also use a URL or a provider file ID:

from micro_agents import Media

result = await session.run(
    "Что изображено и что написано в документе?",
    media=[Media.from_path("photo.png"), Media.from_path("notes.pdf")],
)

Do not interpolate secrets, API keys, or internal-only values into master_prompt: it receives the full live context dict.

Dynamic tools

Use DynamicToolManager when integrations can be connected or disconnected while the application is running:

from micro_agents import DynamicToolManager, Tool

tools = DynamicToolManager()
agent = Aggregator(llm, tools)

tools.add_tool(Tool(current_weather))
tools.remove_tool("current_weather")

Existing sessions read the current registry before every LLM round. Duplicate names are rejected unless add_tool(tool, replace=True) is used.

Context injection for tools

Mark a tool argument with Injected to receive the current session context. That argument is not included in the tool schema sent to the LLM, so the model cannot provide or override it.

from pathlib import Path
from typing import Annotated

from micro_agents import Injected, SessionContext


def read_note(path: str, context: Annotated[SessionContext, Injected]) -> str:
    return (Path(context["base_dir"].__str__()) / path).read_text()

The same aggregator may then create isolated sessions with different roots:

agent.create_session(context={"base_dir": Path("test/alice")})
agent.create_session(context={"base_dir": Path("test/bob")})

DeepSeek filesystem example

Install the optional tool package first:

uv add micro-agents-tools

The runnable module below reads DEEPSEEK_API_KEY, uses DeepSeek's OpenAI-compatible Responses API, and can only read or write files inside the current directory's test folder. Edit PROMPT in the file, then run it:

$env:DEEPSEEK_API_KEY = "..."
uv run --package micro-agents-tools -m micro_agents_tools.examples.deepseek_filesystem

Set DEEPSEEK_MODEL to override the default deepseek-v4-flash. Filesystem and opt-in Python tools are in the separate micro-agents-tools package. Filesystem tools deliberately have no delete operation, and reject absolute paths, .. escapes, and symlinks that resolve outside test.

PythonTools is opt-in: add *PythonTools().tools() to the manager only when the application trusts the code being generated. It runs a subprocess in base_dir with a timeout and bounded output, but it is not a security sandbox; use a container or VM for untrusted code.

FilesystemTools also exposes attach_files(paths). The model can select images or documents under the session's base_dir; they are returned as real tool-output attachments and become available to the model on the next tool-loop round. Paths remain confined to base_dir, and the combined attachment size is limited to 50 MiB by default.

BraveSearchTools().tools() adds a web_search tool backed by Brave Search. Set BRAVE_SEARCH_API_KEY in the application environment; the key never becomes a model argument or a session-context value.

Description
No description provided
Readme 246 KiB
Languages
Python 100%