agentleFS
lauren-frameworklauren-framework/lauren-ai/skills/async-agent-background/SKILL.md

async-agent-background

Skill0 starsChanged 6 months agoMITView on GitHub

Submit agent tasks to an asyncio queue and process them with a background worker, returning results by task ID. Use when you need non-blocking agent execution in web handlers, batch processing pipelines, or any situation where callers should not wait for agent completion.

The file

Files
SKILL.md

Use codemap find "AgentTaskQueue" after adding the pattern to your project.

Async Agent Task Execution with Background Workers

Submit prompts as tasks, let a background worker run the agent, and poll for results by task ID.

Pattern

import asyncio
from dataclasses import dataclass, field
from uuid import uuid4

@dataclass
class AgentTask:
    task_id: str
    prompt: str
    status: str = "pending"   # pending | running | done | failed
    result: str | None = None
    error: str | None = None

class AgentTaskQueue:
    def __init__(self):
        self._queue: asyncio.Queue = asyncio.Queue()
        self._tasks: dict[str, AgentTask] = {}
        self._worker_task: asyncio.Task | None = None

    async def submit(self, prompt: str) -> str:
        task = AgentTask(task_id=str(uuid4()), prompt=prompt)
        self._tasks[task.task_id] = task
        await self._queue.put(task)
        return task.task_id

    async def get_result(self, task_id: str) -> AgentTask | None:
        return self._tasks.get(task_id)

    async def start_worker(self, handler) -> None:
        self._worker_task = asyncio.create_task(self._run_worker(handler))

    async def stop_worker(self) -> None:
        if self._worker_task:
            self._worker_task.cancel()
            try:
                await self._worker_task
            except asyncio.CancelledError:
                pass

    async def drain(self) -> None:
        """Wait until all queued tasks have been processed."""
        await self._queue.join()

    async def _run_worker(self, handler) -> None:
        while True:
            task = await self._queue.get()
            task.status = "running"
            try:
                task.result = await handler(task.prompt)
                task.status = "done"
            except Exception as e:
                task.error = str(e)
                task.status = "failed"
            finally:
                self._queue.task_done()

Integration with AgentRunner

from lauren_ai._agents._runner import AgentRunnerBase as AgentRunner
from lauren_ai._config import LLMConfig
from lauren_ai._transport._mock import MockTransport

mock = MockTransport()
cfg = LLMConfig(provider="anthropic", model="mock-model", api_key="mock")
runner = AgentRunner(transport=mock)

@agent(model="claude-sonnet-4-6", system="You are a helpful assistant.")
class BackgroundAgent: ...

agent_instance = BackgroundAgent()

async def run_agent(prompt: str) -> str:
    response = await runner.run(agent_instance, prompt)
    return response.content

queue = AgentTaskQueue()
await queue.start_worker(run_agent)

task_id = await queue.submit("Summarise this document...")
await queue.drain()
result = await queue.get_result(task_id)
print(result.status, result.result)

Lauren web handler pattern

@post("/tasks")
async def submit_task(req: Request) -> dict:
    body = await req.json()
    task_id = await queue.submit(body["prompt"])
    return {"task_id": task_id, "status": "pending"}

@get("/tasks/{task_id}")
async def get_task(task_id: str) -> dict:
    task = await queue.get_result(task_id)
    if task is None:
        raise HTTPError(404, detail="Task not found")
    return {"task_id": task.task_id, "status": task.status, "result": task.result}

Notes

  • asyncio.Queue.join() / task_done() ensure reliable drain-and-wait.
  • For persistence across restarts, swap the in-memory queue for a Redis stream or a database-backed queue (e.g. pgqueue).
  • Concurrency: start multiple workers with asyncio.create_task in a loop.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.