From Prompt to Paycheck: Wiring an LLM Chain Into Real Gig Platforms
Building autonomous agents that can earn money isn’t science‑fiction—it’s a matter of connecting a language model to the APIs that power freelance marketplaces. This post walks through a minimal, production‑style pipeline: a prompt‑driven LLM chain, a thin wrapper that turns model output into concrete platform actions, and the practical concerns you’ll hit when you try to run it at scale.
1. The Core Idea
A gig platform (e.g., a job board, a micro‑task site, or a custom SaaS marketplace) typically exposes:
| Action | Typical endpoint | What you need to send | What you get back |
|---|---|---|---|
| List open tasks | GET /tasks?status=open |
Auth token, optional filters | JSON array of task objects |
| Claim a task | POST /tasks/{id}/claim |
Auth token, worker ID | Confirmation or error |
| Submit work | POST /tasks/{id}/submit |
Auth token, result payload | Payment status, reviewer feedback |
| Get payout | GET /payouts?worker={id} |
Auth token | Balance, pending transactions |
If you can replace the human decision‑maker in the loop with an LLM that (1) reads a task description, (2) decides whether it’s worth doing, (3) produces the required artifact, and (4) posts the result, you have an autonomous earning agent. The chain looks like:
Prompt → LLM → (optional) Tool use → Action API → Feedback → (loop)
The rest of this article shows a concrete implementation in Python, using the lightweight [LangChain Expression Language (LCEL)] for the prompt‑model part and httpx for async HTTP calls to the platform.
2. Prerequisites
- Python 3.11+
- An LLM endpoint that supports chat completions (OpenAI‑compatible, Anthropic, or a self‑hosted model served via TGI/vLLM).
- Credentials for the target gig platform (usually a bearer token or API key).
- A small amount of USDC on Base for the x402 payment demo (see the final note).
Install the core deps:
pip install langchain langchain-core httpx[http2] python-dotenv
3. Prompt Engineering – From Raw Description to Structured Plan
The model does not need to know the internals of the platform; it only needs to output a plan that our wrapper can execute. A simple JSON schema works well:
{
"task_id": "string",
"action": "claim | submit | skip",
"payload": { /* depends on action */ },
"confidence": 0.0-1.0
}
We ask the model to fill this schema using a few‑shot prompt. Below is the prompt template (store it in prompt.txt):
You are an autonomous worker for a gig platform.
Given the following task description, decide whether to claim it, skip it, or (if already claimed) submit a result.
Respond ONLY with a JSON object that matches the schema:
{
"task_id": "<string>",
"action": "claim | submit | skip",
"payload": { /* see notes */ },
"confidence": <float between 0 and 1>
}
Notes:
- If action is "claim", payload can be empty.
- If action is "submit", payload must contain a "result" field with the work product (e.g., generated text, image URL, code snippet).
- If action is "skip", payload can be empty.
- Set confidence to reflect how sure you are about the decision.
Examples:
Task: "Write a 150‑word blog intro about renewable energy."
{
"task_id": "t123",
"action": "submit",
"payload": { "result": "Renewable energy…" },
"confidence": 0.94
}
Task: "Design a logo for a coffee shop."
{
"task_id": "t456",
"action": "skip",
"payload": {},
"confidence": 0.78
}
4. Wiring the LLM Chain
We’ll use LangChain’s ChatPromptTemplate + a chat model + a JsonOutputParser to enforce the schema. The code below assumes an OpenAI‑compatible endpoint; swap the base_url and api_key for your provider.
# agent.py
import os
import json
import httpx
from dotenv import load_dotenv
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from langchain_openai import ChatOpenAI # replace with your chat model class
load_dotenv() # loads OPENAI_API_KEY, PLATFORM_TOKEN, PLATFORM_BASE_URL
# ----------------------------------------------------------------------
# 1️⃣ Prompt → Model → Structured output
# ----------------------------------------------------------------------
prompt = ChatPromptTemplate.from_template(open("prompt.txt").read())
parser = JsonOutputParser() # validates JSON shape & types
model = ChatOpenAI(
model="gpt-4o-mini", # or any model you have access to
temperature=0.2,
max_tokens=500,
api_key=os.getenv("OPENAI_API_KEY"),
)
chain = prompt | model | parser # LCEL composition
# ----------------------------------------------------------------------
# 2️⃣ Platform client (async for scalability)
# ----------------------------------------------------------------------
PLATFORM_BASE = os.getenv("PLATFORM_BASE_URL") # e.g. https://api.gigexample.com
PLATFORM_TOKEN = os.getenv("PLATFORM_TOKEN")
async def platform_request(method: str, path: str, json_data=None):
headers = {"Authorization": f"Bearer {PLATFORM_TOKEN}"}
async with httpx.AsyncClient(base_url=PLATFORM_BASE, headers=headers, timeout=30.0) as client:
resp = await client.request(method, path, json=json_data)
resp.raise_for_status()
return resp.json()
# ----------------------------------------------------------------------
# 3️⃣ Core loop: fetch tasks, run chain, act
# ----------------------------------------------------------------------
async def process_one_task(task: dict):
task_id = task["id"]
description = task["description"]
# Ask the LLM what to do
try:
decision = await chain.ainvoke({"task_description": description})
except Exception as e:
print(f"[{task_id}] LLM error: {e}")
return
# Basic sanity check
if not isinstance(decision, dict) or "action" not in decision:
print(f"[{task_id}] Invalid decision: {decision}")
return
action = decision["action"]
payload = decision.get("payload", {})
confidence = decision.get("confidence", 0.0)
print(f"[{task_id}] LLM suggests {action} (conf={confidence:.2f})")
# ------------------------------------------------------------------
# Execute the chosen action against the platform
# ------------------------------------------------------------------
try:
if action == "claim":
await platform_request("POST", f"/tasks/{task_id}/claim")
print(f"[{task_id}] Claimed")
elif action == "submit":
# The payload must contain a "result" field per our schema
result = payload.get("result")
if result is None:
raise ValueError("Submit action missing 'result'")
await platform_request(
"POST",
f"/tasks/{task_id}/submit",
{"result": result}
)
print(f"[{task_id}] Submitted")
elif action == "skip":
print(f"[{task_id}] Skipped")
else:
print(f"[{task_id}] Unknown action: {action}")
except httpx.HTTPStatusError as exc:
print(f"[{task_id}] Platform error {exc.response.status_code}: {exc.response.text}")
async def worker_loop(poll_interval: int = 10):
while True:
try:
open_tasks = await platform_request("GET", "/tasks?status=open")
for task in open_tasks:
await process_one_task(task)
except Exception as e:
print(f"Loop error: {e}")
await httpx.AsyncClient().sleep(poll_interval)
if __name__ == "__main__":
import asyncio
asyncio.run(worker_loop())
What this script does
- Fetches all open tasks from the platform (you can add pagination or filters).
- Sends each task’s description to the LLM via the prompt‑chain, receiving a structured decision.
-
Executes the decided action (
claim,submit, orskip) by calling the appropriate platform endpoint. - Loops forever, pausing a configurable interval between polls.
5. Honest Trade‑offs
| Aspect | Benefit | Cost / Risk |
|---|---|---|
| Latency | The LLM call dominates (≈300‑800 ms for a small model). Adding network round‑trips to the platform adds another 100‑200 ms. | Real‑time bidding wars (e.g., “first to claim wins”) may be lost if your poll interval is too high. Reducing interval increases platform load and may get you rate‑limited. |
| Model quality | A 7B‑parameter open‑model can produce decent JSON for simple tasks; larger models improve correctness but raise cost. | Hallucinations can lead to invalid JSON or malformed payloads, causing rejected submissions and possible reputation penalties. Validation (the JsonOutputParser) catches structural errors but not semantic ones (e.g., submitting unrelated text). |
| Funds handling | Earnings accrue in the platform |
Top comments (0)