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 agents that can actually earn money on freelance marketplaces requires more than a clever prompt. Below is a pragmatic walk‑through of the components, the code that glues them together, and the practical trade‑offs you’ll encounter when you try to move from “it works in a notebook” to a service that gets paid in USDC on Base.


1. High‑level Architecture

+-------------------+      +-------------------+      +-------------------+
|  User / Trigger   | ---> |  Prompt Builder   | ---> |   LLM Chain       |
+-------------------+      +-------------------+      +-------------------+
                                   |                         |
                                   v                         v
                          +----------------+        +-------------------+
                          |  Output Parser |        |  Gig‑API Adapter  |
                          +----------------+        +-------------------+
                                   |                         |
                                   v                         v
                          +----------------+        +-------------------+
                          |  Payment Hook  | <----> |  x402 Settlement  |
                          +----------------+        +-------------------+
Enter fullscreen mode Exit fullscreen mode
  • Prompt Builder – turns a high‑level intent (e.g., “write a 500‑word blog intro about renewable energy”) into a structured prompt that the LLM can consume.
  • LLM Chain – a sequence of calls (sometimes with retrieval, sometimes with tool use) that produces the final artifact.
  • Output Parser – validates shape, length, and format; throws an error if the LLM hallucinates something unusable.
  • Gig‑API Adapter – authenticates to the marketplace, creates a job/gig submission, and polls for status.
  • Payment Hook – invokes the x402 settlement flow once the buyer confirms delivery; the agent receives USDC directly to its wallet.

Each block is deliberately isolated so you can swap‑out components (e.g., replace the LLM with a local model, or change the gig platform) without rewriting the whole pipeline.


2. Prompt Engineering & LLM Chain

We’ll use LangChain for its composable primitives, but the same ideas apply to LlamaIndex or a hand‑rolled prompt‑template library.

# prompt_builder.py
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.chat_models import ChatOpenAI   # swap for any compatible endpoint

# 1️⃣ Base template – keep it short, explicit, and versioned.
BASE_TEMPLATE = """
You are a professional freelance writer.
Task: {task_description}
Constraints:
- Length: {min_words}-{max_words} words
- Tone: {tone}
- Format: {output_format}
Do not add any preamble or apology. Return only the requested content.
"""

prompt = PromptTemplate(
    input_variables=["task_description", "min_words", "max_words", "tone", "output_format"],
    template=BASE_TEMPLATE,
)

# 2️⃣ LLM – choose a model whose cost per token fits your pricing window.
llm = ChatOpenAI(
    model_name="gpt-4o-mini",          # cheap enough for $0.01‑$0.10 per call
    temperature=0.3,
    max_tokens=800,                    # safety net to avoid runaway generation
)

# 3️⃣ Chain – you can insert retrieval, tool use, or self‑critique steps here.
writer_chain = LLMChain(llm=llm, prompt=prompt)
Enter fullscreen mode Exit fullscreen mode

Why this works (and where it frays)

Aspect Benefit Trade‑off
Explicit constraints in the prompt reduce hallucination and make output parsing easier. Still relies on the model to obey length/tone; occasional overruns happen.
Low‑cost model (gpt‑4o‑mini) keeps per‑call cost in the target band. Slightly lower reasoning depth; complex multi‑step tasks may need a stronger model, raising cost.
Deterministic temperature (0.3) gives repeatable outputs for testing. Too low temperature can make the output feel stale; you may need to tune per‑task.

If you need retrieval (e.g., pulling a style guide from a knowledge base), slot a RetrievalQA step before the LLMChain. If you want the model to call external tools (e.g., a grammar checker), wrap the chain in an Agent that can invoke those tools between LLM calls.


3. Parsing & Validation

The gig platform will reject malformed submissions, so we validate before we ever hit their API.

# output_parser.py
import re
from pydantic import BaseModel, ValidationError, field_validator

class GigOutput(BaseModel):
    text: str
    word_count: int

    @field_validator("text")
    @classmethod
    def not_empty(cls, v):
        if not v.strip():
            raise ValueError("Output text is empty")
        return v

    @field_validator("word_count")
    @classmethod
    def matches_text(cls, v, info):
        actual = len(info.data["text"].split())
        if actual != v:
            raise ValueError(f"Declared word count {v} does not match actual {actual}")
        return v

def parse_and_validate(raw: str, min_words: int, max_words: int) -> GigOutput:
    # Very naive word count; replace with a proper tokenizer if needed.
    wc = len(raw.split())
    if not (min_words <= wc <= max_words):
        raise ValueError(f"Word count {wc} outside allowed range [{min_words},{max_words}]")
    return GigOutput(text=raw.strip(), word_count=wc)
Enter fullscreen mode Exit fullscreen mode

Observations

  • The parser catches the two most common failure modes: empty output and length mismatch.
  • Adding a schema (Pydantic) gives you automatic JSON‑serialization for later storage or audit logs.
  • If you need richer structure (e.g., markdown with front‑matter), extend the model accordingly—just keep the validation logic tight; otherwise you’ll spend more time debugging parsing errors than actually earning.

4. Gig‑Platform Adapter (Fiverr Example)

Most marketplaces expose a REST API for creating a “gig” or submitting a proposal. Below is a minimal adapter for Fiverr’s Seller API (you’ll need an API key and OAuth token). Replace the endpoint and payload fields for Upwork, Freelancer, or a custom gig board.

# fiverr_adapter.py
import httpx
from typing import Dict

FIVERR_BASE = "https://api.fiverr.com/v1"

class FiverrAdapter:
    def __init__(self, access_token: str):
        self.client = httpx.AsyncClient(
            base_url=FIVERR_BASE,
            headers={"Authorization": f"Bearer {access_token}"},
            timeout=httpx.Timeout(10.0, read=30.0),
        )

    async def create_gig(
        self,
        title: str,
        description: str,
        category_id: int,
        price_usd: float,
    ) -> Dict:
        payload = {
            "title": title,
            "description": description,
            "category_id": category_id,
            "price": {
                "amount": round(price_usd, 2),
                "currency": "USD",
            },
            # Optional: delivery time, revisions, etc.
        }
        resp = await self.client.post("/gigs", json=payload)
        resp.raise_for_status()
        return resp.json()

    async def get_gig_status(self, gig_id: str) -> Dict:
        resp = await self.client.get(f"/gigs/{gig_id}")
        resp.raise_for_status()
        return resp.json()
Enter fullscreen mode Exit fullscreen mode

Key points

  • Async client – lets you pipeline multiple LLM calls while waiting for the platform’s rate‑limited endpoints.
  • Explicit timeout – prevents a hung request from blocking your event loop.
  • Error propagationraise_for_status() turns HTTP 4xx/5xx into exceptions that your orchestrator can catch and turn into a payment‑refund or retry logic.

5. x402 Payment Settlement

The x402 protocol lets you attach a micropayment to an HTTP response. When the buyer confirms delivery (via a webhook you expose), you respond with an x402 header that triggers the settlement on Base. The snippet below shows how to construct that response using the x402-py helper library (a thin wrapper around the spec).


python
# payment_hook.py
from x402 import create_payment_response, PaymentRequired
from eth_account import Account
import os

# Your agent’s wallet – keep the private key in a secret manager, never in code.
WALLET_PRIVATE_KEY = os.getenv("AGENT_WALLET_PRIV")
ACCOUNT = Account.from_key(WALLET_PRIVATE_KEY) if WALLET_PRIVATE_KEY else None

async def settle_payment(amount_usdc: float, buyer_address: str) -> dict:
    """
    Returns a dict suitable for sending as an HTTP response body.
    The caller must attach the `x402` header returned by create_payment_response.
    """
    if ACCOUNT is None:
        raise RuntimeError("Wallet not configured")

    # x402 expects amount in the smallest unit (USDC has 6 decimals)
    amount_wei = int(amount_usdc * 1_000_000)

    # Build the payment request – the buyer will sign and send the transaction.
    payment_req = PaymentRequired(
        amount=amount_wei,
        asset="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",  # USDC on Base
        payee=ACCOUNT.address,
        chain
Enter fullscreen mode Exit fullscreen mode

Top comments (0)