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 accept work, perform tasks, and get paid is no longer a sci‑fi thought experiment. The pieces exist—large language models, tool‑calling frameworks, and micropayment protocols—but stitching them together requires careful engineering. Below is a pragmatic walk‑through of how to turn a prompt‑driven LLM chain into a billable service that can be offered on gig‑style marketplaces (Upwork, Fiverr, or a custom job board).


1. High‑level Architecture

+----------------+      +-------------------+      +-------------------+
|  Gig Platform  | <--->|   Agent Frontend  | <--->|   LLM Orchestrator|
| (job post,     |      | (webhook / API)   |      | (LangChain +      |
|  payout)       |      |                   |      |  x402 payment)    |
+----------------+      +-------------------+      +-------------------+
Enter fullscreen mode Exit fullscreen mode
  • Gig Platform – posts a job, sends a JSON payload to a webhook you expose, and later releases payment when you signal completion.
  • Agent Frontend – a thin HTTP service (e.g., a Cloudflare Worker or FastAPI app) that validates the incoming request, adds authentication, and forwards the job description to the orchestrator.
  • LLM Orchestrator – the core where the prompt chain runs, tools are invoked, and the x402 micropayment protocol is used to charge the client per call or per completed unit of work.

The flow is synchronous for simplicity: the client waits for the agent to finish and returns the result in the same HTTP response. If you need longer‑running work, replace the synchronous response with a job ID and a polling endpoint.


2. Choosing the LLM Stack

For reproducibility, I’ll use LangChain (v0.2) with OpenAI’s GPT‑4‑turbo as the base model. The same pattern works with any model that supports function calling (Anthropic Claude, Mistral, local Llama‑3 via TGI, etc.).

# orchestrator.py
import os
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain.tools import Tool
from langchain.memory import ConversationBufferWindowMemory

llm = ChatOpenAI(
    model_name="gpt-4-turbo-preview",
    temperature=0.2,
    openai_api_key=os.getenv("OPENAI_API_KEY"),
)

# Example tool: fetch a public GitHub repo and summarize its README
def github_readme_summary(url: str) -> str:
    import requests, re
    # Extract owner/repo from a typical GitHub URL
    m = re.search(r"github\.com[:/]([^/]+)/([^/]+)", url)
    if not m:
        return "Invalid GitHub URL"
    owner, repo = m.groups()
    api = f"https://api.github.com/repos/{owner}/{repo}/readme"
    r = requests.get(api, headers={"Accept": "application/vnd.github.v3.raw"})
    if r.status_code != 200:
        return f"Failed to fetch README: {r.status_code}"
    # Very naive summary – replace with a proper summarization chain if needed
    return r.text[:1500] + ("" if len(r.text) > 1500 else "")

github_tool = Tool(
    name="GitHubReadmeSummarizer",
    func=github_readme_summary,
    description="Given a GitHub repository URL, returns a concise summary of its README.",
)

tools = [github_tool]

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant that can use tools to answer user questions."),
    MessagesPlaceholder(variable_name="chat_history", optional=True),
    ("human", "{input}"),
    MessagesPlaceholder(variable_name="agent_scratchpad"),
])

memory = ConversationBufferWindowMemory(
    memory_key="chat_history",
    return_messages=True,
    k=5,  # keep last 5 exchanges
)

agent = create_openai_functions_agent(llm, tools, prompt)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    memory=memory,
    verbose=False,
    max_iterations=5,
    early_stopping_method="generate",
)
Enter fullscreen mode Exit fullscreen mode

Why this setup?

  • Deterministic tool use – the agent only calls tools when the LLM decides it’s needed, reducing hallucination.
  • Memory window – a sliding window keeps context cheap (no unlimited token growth) while still allowing multi‑turn clarification.
  • Verbose off – in production you’ll want structured logs, not stdout spam.

3. Adding Micropayment Logic (x402)

The x402 protocol lets you attach a payment requirement to an HTTP response (402 Payment Required). The client (gig platform or a frontend) must include a valid X-Payment header with a signed USDC transaction on Base.

We’ll use the x402-py helper (a thin wrapper around the official spec). It validates the header, extracts the amount, and optionally forwards the payment to a custodial wallet.

# payment.py
from x402 import verify_payment, PaymentError
import os

# USDC contract on Base (mainnet) – replace with testnet if you experiment
USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
# Your receiver address (the agent operator)
RECEIVER = os.getenv("AGENT_WALLET_ADDRESS")  # e.g., 0xAbc...

def require_payment(amount_usdc: float):
    """
    Decorator for FastAPI endpoints.
    Returns a 402 response if payment is missing or invalid.
    """
    def decorator(func):
        async def wrapper(*args, **kwargs):
            # `request` is injected by FastAPI
            request = kwargs.get("request")
            if request is None:
                # Try to get it from args (FastAPI passes it as first positional)
                request = args[0] if args else None
            if request is None:
                raise RuntimeError("request object not found in decorated endpoint")

            try:
                verify_payment(
                    request=request,
                    receiver=RECEIVER,
                    token_address=USDC_ADDRESS,
                    amount=amount_usdc,
                )
            except PaymentError as e:
                # Return a 402 with the required payment details
                from fastapi import Response
                return Response(
                    status_code=402,
                    headers={
                        "WWW-Authenticate": f'X402 token="{USDC_ADDRESS}", '
                                            f'amount="{amount_usdc}", '
                                            f'network="base", '
                                            f'payload="{e.payload}"',
                    },
                    content="Payment required",
                )
            # Payment OK – call the actual handler
            return await func(*args, **kwargs)
        return wrapper
    return decorator
Enter fullscreen mode Exit fullscreen mode

Trade‑offs

Aspect Choice Reason Downside
Payment granularity Per‑API‑call (e.g., $0.02 per LLM + tool use) Simple to reason about; matches micropayment model Overheads if a job needs many calls; client may prefer per‑job pricing
Currency USDC on Base Low fees (~$0.0001), fast finality, widely supported wallets Requires the client to hold USDC and understand Base; adds onboarding friction
Custodial vs. non‑custodial Non‑custodial (agent holds funds in its own wallet) No third‑party risk, immediate settlement Operator must manage key security and compliance (KYC/AML if scaling)
Retry logic Idempotent payment verification (using nonce) Prevents double‑charging on retries Requires storing nonces or relying on the protocol’s built‑in replay protection

If you prefer a simpler model, you can skip x402 entirely and invoice via traditional platforms (PayPal, Stripe) after the job is marked complete. The x402 path shines when you want instant, trustless settlement directly from the gig platform’s frontend.


4. Exposing the Agent as a Gig‑Ready Endpoint

Below is a minimal FastAPI app that ties the orchestrator and payment decorator together. The agent accepts a JSON payload { "task": "summarize the README of https://github.com/owner/repo" } and returns the LLM’s answer.

# main.py
from fastapi import FastAPI, Request, HTTPException
from pydantic import BaseModel
from orchestrator import agent_executor
from payment import require_payment

app = FastAPI(title="LLM Gig Agent")

class TaskPayload(BaseModel):
    task: str

@app.post("/run")
@require_payment(amount_usdc=0.02)  # $0.02 per invocation
async def run_task(payload: TaskPayload, request: Request):
    try:
        # The agent_executor is synchronous; we run it in a threadpool to avoid blocking
        import asyncio
        result = await asyncio.to_thread(agent_executor.run, payload.task)
        return {"output": result}
    except Exception as exc:
        # Log the error (use structlog or similar in production)
        raise HTTPException(status_code=500, detail=str(err))
Enter fullscreen mode Exit fullscreen mode

Deploying

  • Serverless – Push the app to Cloudflare Workers (via wranger + js shim) or AWS Lambda with a container image. Cold start latency (~200‑400 ms) is acceptable for most gig‑type interactions.
  • Scaling – Because each request is stateless (except the in‑memory conversation window, which is scoped to the request), you can horizontally scale behind a simple API gateway.

Top comments (0)