DEV Community

Nikhil Ranka
Nikhil Ranka

Posted on

From Prompt to Paycheck: Wiring an LLM Chain Into Real Gig Platforms

From Prompt to Paycheck: Wiring an LLM Chain Into Real Gig Platforms

Building autonomous AI agents that can earn money on freelance marketplaces requires more than a clever prompt. You need reliable wiring between the language model, the gig‑platform API, and a payment mechanism that actually settles in real‑world currency. Below is a step‑by‑step walk‑through of a minimal, production‑ish implementation, the trade‑offs you’ll hit along the way, and concrete code you can run today.


1. Why a “chain” and not just a single prompt?

A freelance job typically looks like this:

  1. Discover a task (search or webhook).
  2. Understand the requirements (parse description, detect needed skills).
  3. Produce a deliverable (code, design, copy).
  4. Submit the result via the platform’s API.
  5. Confirm payment or request revision.

If you try to cram all of that into one monolithic prompt you’ll quickly hit token limits, hallucination spikes, and debugging nightmares. Splitting the workflow into discrete LLM‑powered steps (a chain) lets you:

  • Reuse prompts and models tuned for each sub‑task.
  • Swap out a slow, expensive model for a faster, cheaper one where precision isn’t critical.
  • Isolate failures (e.g., a bad parse) without re‑running the whole pipeline.

The downside is added latency and more moving parts that need monitoring, but for most gig‑type workloads the trade‑off is worth it.


2. High‑level architecture

+----------------+      +----------------+      +----------------+
|   Scheduler    | -->  |  Task Fetcher  | -->  |  Requirement   |
| (cron/webhook) |      | (platform API) |      |  Parser (LLM)  |
+----------------+      +----------------+      +----------------+
                                 |                     |
                                 v                     v
                       +----------------+      +----------------+
                       |  Skill Matcher | -->  |  Generator (LLM)|
                       +----------------+      +----------------+
                                 |                     |
                                 v                     v
                       |
                       +----------------+                |
                       |  Validator    |<---------------+
                       +----------------+
                                 |
                                 v
                       +----------------+
                       |  Submitter    |
                       +----------------+
                                 |
                                 v
                       +----------------+
                       |  Payment Hook  |
                       +----------------+
Enter fullscreen mode Exit fullscreen mode
  • Scheduler – a lightweight cron job or Cloudflare Worker that polls the gig platform’s “new‑job” endpoint (or listens to a webhook).
  • Task Fetcher – pulls the raw JSON payload (title, description, budget, required skills).
  • Requirement Parser – LLM‑based Named Entity Recognition that extracts a structured spec (e.g., {language: "Python", lib: "pandas", output: "CSV"}); we’ll use a small, cheap model here because the task is mostly pattern matching.
  • Skill Matcher – deterministic logic that compares the parsed spec against the agent’s capability catalog (a simple CSV or SQLite table). If there’s no match, the job is skipped.
  • Generator – the heavyweight LLM that actually creates the artifact (code snippet, design mockup, copy). This is where you’ll likely spend the most tokens.
  • Validator – runs unit tests, lints, or a lightweight “does‑it‑look‑right” check using another LLM or static analysis.
  • Submitter – posts the artifact back to the gig platform via its API (often a multipart/form‑data upload or a JSON payload).
  • Payment Hook – triggers the x402 micro‑payment flow; the platform itself may handle escrow, but we expose a callback that signs an x402 invoice for the agreed amount.

3. Working code snippets

Below is a self‑contained Python 3.11 example that demonstrates the core chain using LangChain‑style composability (you can replace LangChain with plain function calls if you prefer fewer dependencies).


python
# -------------------------------------------------
# 1. Imports & configuration
# -------------------------------------------------
import os
import json
import httpx
from typing import Dict, Any
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain_community.llms import Ollama   # local Llama‑2 7B for cheap steps
from langchain_openai import ChatOpenAI       # GPT‑4‑turbo for generation (costly)

# Environment variables you must set:
# OPENAI_API_KEY, GIG_PLATFORM_TOKEN, GIG_PLATFORM_BASE_URL
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
PLATFORM_TOKEN = os.getenv("GIG_PLATFORM_TOKEN")
PLATFORM_BASE  = os.getenv("GIG_PLATFORM_BASE_URL", "https://api.example-gig.com")

# -------------------------------------------------
# 2. Helper: fetch newest job (simplified)
# -------------------------------------------------
async def fetch_new_job() -> Dict[str, Any]:
    async with httpx.AsyncClient() as client:
        resp = await client.get(
            f"{PLATFORM_BASE}/jobs?status=new&limit=1",
            headers={"Authorization": f"Bearer {PLATFORM_TOKEN}"},
        )
        resp.raise_for_status()
        data = resp.json()
        return data["jobs"][0] if data["jobs"] else {}

# -------------------------------------------------
# 3. Requirement Parser (cheap, local LLM)
# -------------------------------------------------
parser_prompt = PromptTemplate(
    input_variables=["description"],
    template=(
        "You are a job‑spec extractor. Return a JSON object with the keys: "
        "`language`, `framework`, `output_type`. If a field is unknown, set it to null.\n\n"
        "Job description:\n{description}\n\nJSON:"
    ),
)
parser_llm = Ollama(model="llama2")   # ~7B parameters, runs locally, ~0 cost per call
parser_chain = LLMChain(llm=parser_llm, prompt=parser_prompt)

async def parse_requirements(job: Dict[str, Any]) -> Dict[str, Any]:
    result = await parser_chain.ainvoke({"description": job["description"]})
    # The LLM may output extra text; try to parse the first JSON block.
    try:
        spec = json.loads(result["text"].strip().split("\n")[0])
    except json.JSONDecodeError:
        spec = {"language": None, "framework": None, "output_type": None}
    return spec

# -------------------------------------------------
# 4. Skill Matcher (deterministic)
# -------------------------------------------------
CAPABILITIES = {
    ("Python", "pandas", "CSV"): True,
    ("JavaScript", "React", "HTML"): True,
    # add more as needed
}

def skill_match(spec: Dict[str, Any]) -> bool:
    key = (spec.get("language"), spec.get("framework"), spec.get("output_type"))
    return CAPABILITIES.get(key, False)

# -------------------------------------------------
# 5. Generator (expensive, remote LLM)
# -------------------------------------------------
gen_prompt = PromptTemplate(
    input_variables=["spec"],
    template=(
        "Write a complete, production‑ready {language} script that uses {framework} "
        "to produce a {output_type} file. Include necessary imports, error handling, "
        "and a brief README comment at the top.\n\nSpec: {spec}"
    ),
)
gen_llm = ChatOpenAI(model="gpt-4-turbo", temperature=0.2)
gen_chain = LLMChain(llm=gen_llm, prompt=gen_prompt)

async def generate_artifact(spec: Dict[str, Any]) -> str:
    result = await gen_chain.ainvoke({"spec": json.dumps(spec)})
    return result["text"]

# -------------------------------------------------
# 6. Validator (simple static check)
# -------------------------------------------------
async def validate_artifact(code: str, spec: Dict[str, Any]) -> bool:
    # For Python we can run a quick syntax check; for other languages adapt.
    if spec.get("language") == "Python":
        try:
            compile(code, "<generated>", "exec")
            return True
        except SyntaxError:
            return False
    # Placeholder: assume valid for non‑Python
    return True

# -------------------------------------------------
# 7. Submitter (platform API)
# -------------------------------------------------
async def submit_artifact(job_id: str, artifact: str, file_name: str = "solution.py"):
    # Many platforms accept a base64‑encoded file in a multipart request.
    files = {"file": (file_name, artifact, "text/plain")}
    data = {"job_id": job_id, "status": "completed"}
    async with httpx.AsyncClient() as client:
        resp = await client.post(
            f"{PLATFORM_BASE}/submissions",
            headers={"Authorization": f"Bearer {PLATFORM_TOKEN}"},
            files=files,
            data=data,
        )
        resp.raise_for_status()
        return resp.json()

# -------------------------------------------------
# 8. Payment hook (x402 invoice signing)
# -------------------------------------------------
def create_x402_invoice(amount_usdc: float, receiver: str) -> str:
    """
    Very tiny stub: in reality you’d use the x402 SDK to sign an
    EIP‑712 invoice with your wallet’s private key.
    Returns a base64‑encoded signed invoice that the platform can
    verify and settle on Base.
    """
    # Placeholder implementation – replace with real x402 call.
    import base64, json, time
    payload = {
        "type": "x402",
        "amount": str(int(amount_usdc * 1_000_000)),  # USDC has 6 decimals
        "receiver": receiver,
        "timestamp":
Enter fullscreen mode Exit fullscreen mode

Top comments (0)