Skip to content

Repository files navigation

Industrial Rental Ops Agent

AI-powered operations assistant for an industrial rental marketplace (dumpsters, portable toilets, fencing). Helps ops staff triage issues by querying orders, checking customer sentiment, and finding active rentals — all through natural language.

Ops Agent Demo

Quick Start

echo "ANTHROPIC_API_KEY=sk-ant-..." > .env
docker compose up --build

Open http://localhost:3000

Tech Stack

Backend: .NET 10, ASP.NET Core Minimal API, Entity Framework Core (SQLite), Anthropic C# SDK (official)

Frontend: React 19, TypeScript, Vite, Tailwind CSS v4, shadcn/ui, @agenisea/sse-kit (npm)

LLM: Claude via Anthropic API — raw Messages API with OutputConfig (constrained JSON decoding), prompt caching, multi-tool support

Testing: xUnit, FluentAssertions, Microsoft.Extensions.AI.Evaluation (with custom InjectionResistanceEvaluator)

Deployment: Docker Compose (split services: nginx frontend + .NET backend + optional observability)

Capabilities

Tool Description Example
lookup_order Order details by code — status, product, tonnage, access/gate codes, dates "What's the status of ORD-5353?"
get_order_sentiment Sentiment analysis from customer messages — overall score + flagged messages "How's the customer feeling about ORD-9910?"
find_active_orders All active rentals for a company — joins users to orders by company name "Show me active orders for Chase Construction"

Claude can call multiple tools in a single turn (e.g., "Give me the full picture on ORD-9910" triggers both order lookup and sentiment analysis).

How It Works

The agent calls the Anthropic Messages API directly with tool definitions and OutputConfig for constrained JSON decoding. CSV data is loaded into SQLite at startup and queried through C# tool methods. The LLM never sees raw data in its context, so this scales to millions of rows.

Responses stream via .NET 10's native Server-Sent Events API (TypedResults.ServerSentEvents with Channel<SseItem<string>>) so the user sees real-time progress:

event: thinking   -> data: {"message": "Processing your request..."}
event: tool_call  -> data: {"message": "Looking up order ORD-5353..."}
event: complete   -> data: {"message": "...", "details": [...], "sessionId": "..."}

Multi-turn conversations are supported via sessionId — the agent resolves references like "it", "that order", "that company" across turns. Conversation history is stored as plain text matching what the user sees in the chat, with automatic trimming on user/assistant pair boundaries to prevent context window overflow.

Architecture

Browser -> ChatRequest -> POST /api/chat -> ASP.NET Core (native SSE)
                                              |
                                       ChatPipelineService
                                       (injection detection, history, validation)
                                              |
                                         AgentRunner
                                       (provider-agnostic tool loop)
                                              |
                                        IModelClient
                                       (provider boundary)
                                              |
                                       Claude selects tool(s)
                                              |
                                       +------+------+
                                       |  OpsPlugin  |
                                       |  (EF Core)  |
                                       +------+------+
                                              |
                                       AgentResponse
                                       { message, details[] }
                                       (flat OutputConfig schema)
                                              |
                                  +-----------+-----------+
                                  |                       |
                           Plain text history       SSE -> Browser
                           (conversation store)     (thinking/tool_call/complete)

Clean Architecture layers:

  • OpsAgent.Core — Domain entities, schemas (records), interfaces (IModelClient, IConversationStore), telemetry, injection detection. No dependencies.
  • OpsAgent.Infrastructure — EF Core, repositories, AnthropicModelClient (provider adapter), AnthropicTurnBuilder, conversation store, OTel spans. Testing/ subfolder contains DeterministicModelClient for E2E tests.
  • OpsAgent.AgentAgentRunner (provider-agnostic tool loop), ChatPipelineService (orchestration), system prompt, structured output, tool dispatch.
  • OpsAgent.Api — Composition root, endpoints, DI wiring, rate limiting, SSE concurrency middleware, OTel SDK config.

Security

Defense-in-depth against prompt injection and data leakage:

Layer What it does
Input detection InjectionDetector — regex patterns + typoglycemia fuzzy matching, per-session rate limiting
Tool argument validation Regex allowlists (ORD-\d{4} for order codes, alphanumeric for company names)
Tool output sanitization TelemetryRedactor.SanitizeToolOutput — strips instruction-like tags, wraps injection phrases
System prompt hardening Dedicated Security Rules section with data boundary markers (<tool_data> tags)
Output validation StructuredOutput.ValidateResponse — checks for leaked tool names, system prompt fragments
Telemetry redaction PII scanning (email, phone, SSN, CC, API keys), company names hashed, user messages never logged

Testing

Tier Count LLM What
Unit tests 95 No Repositories, plugin methods, schema sync, injection detection, PII scanning, output validation, history trimming, pipeline orchestration, tool dispatch
Integration tests 10 No Real HTTP pipeline via WebApplicationFactory — SSE streaming, session continuity, rate limiting, tool-call flow, injection threshold
Playwright E2E 9 No Browser automation — chat flow, tool status, session continuity, error handling, rate limiting, page reload
Tool evals 7 No Deterministic tool output against seeded SQLite
Agent evals 10 Yes Full pipeline: order lookup, sentiment, company search, off-topic rejection, multi-tool, prompt injection (DAN jailbreak, system prompt extraction, data exfiltration)

Security evals use Microsoft.Extensions.AI.Evaluation with a custom InjectionResistanceEvaluator that scores responses 1-5 for prompt leak and persona adoption. Results are stored to disk and can be viewed as an HTML report.

# Unit + integration tests (safe, no API key)
cd backend && dotnet test tests/OpsAgent.Tests

# Tool evals (safe, no API key)
cd backend && dotnet test evals/OpsAgent.Evals --filter "Category=ToolEval"

# Agent evals (requires ANTHROPIC_API_KEY)
cd backend && ANTHROPIC_API_KEY=... dotnet test evals/OpsAgent.Evals --filter "Category=Integration"

# Playwright E2E (starts own backend in Testing mode, no API key)
cd frontend && pnpm test:e2e

# All backend tests except agent evals
cd backend && dotnet test --filter "Category!=Integration"

Data Model

User (10)              Order (20)                Product (3)
+----------+    1:N    +-----------------+  N:1   +------------------+
| id       |<----------| user_id (FK)    |------->| id               |
| username |           | code  "ORD-"    |        | name             |
| is_active|           | status          |        | included_tonnage |
+----------+           | waste_type_id   |        |  _quantity       |
                       | access_details  |        +------------------+
                       | conversation_id-|
                       | start/end_date  |
                       +-----------------+
                                |
                           1:N  |  (via conversation_id)
                                v
                           Message (60)
                       +-----------------+
                       | conversation_id |
                       | message         |
                       | sentiment_label |
                       | created_on      |
                       +-----------------+

All seed data is synthetic (generated UUIDs, @example.com emails, generic company names).

Docker Profiles

# Dev: frontend (nginx) + backend (.NET) — 2 containers
docker compose up --build

# Lean: + Jaeger tracing UI at :16686 — 3 containers
docker compose --profile lean up --build

# Full: + Langfuse AI observability at :3100 — 8 containers
docker compose --profile full up --build

# Both: dual export (Jaeger + Langfuse) — 9 containers
docker compose --profile lean --profile full up --build
Service Port Profile
Frontend (nginx) 3000 always
Backend (.NET) 8000 always
Jaeger UI 16686 lean
Langfuse UI 3100 full

For Langfuse first-time setup, see ARCHITECTURE_INSIGHTS.md.

Note: docker compose down -v deletes the Langfuse database and all stored credentials. Use docker compose down (without -v) to preserve data across restarts. After a -v reset, create a new Langfuse account and API keys via the UI at :3100 and update .env.

Local Development

# Backend (run from backend/)
cd backend && dotnet build
export ANTHROPIC_API_KEY=sk-ant-...
cd backend && dotnet run --project src/OpsAgent.Api

# Frontend (separate terminal)
cd frontend && pnpm install && pnpm dev
# Dev server proxies /api -> localhost:8000

Project Structure

backend/
  Dockerfile                 # .NET backend container (non-root)
  src/
    OpsAgent.Core/           # Domain: entities, schemas, interfaces, telemetry, injection detection
    OpsAgent.Infrastructure/ # Data: EF Core, repos, AnthropicModelClient, AnthropicTurnBuilder, conversation store
      AI/                    # Provider adapters (Anthropic)
      Testing/               # DeterministicModelClient for E2E tests (inert in production)
    OpsAgent.Agent/          # App: AgentRunner, ChatPipelineService, system prompt, tool dispatch
    OpsAgent.Api/            # API: endpoints, DI, rate limiting, SSE concurrency middleware, OTel SDK
  tests/
    OpsAgent.Tests/          # Unit + integration tests (xUnit + FluentAssertions)
      IntegrationTests/      # WebApplicationFactory HTTP pipeline tests
      TestDoubles/           # Shared FakeModelClient
  evals/
    OpsAgent.Evals/          # Tool evals + agent evals (Microsoft.Extensions.AI.Evaluation)
  data/                      # CSV seed files

frontend/
  Dockerfile                 # nginx frontend container (non-root)
  nginx.conf                 # Reverse proxy + rate limiting + SSE support
  e2e/                       # Playwright browser tests
  src/
    components/chat/         # Chat UI (window, messages, input, copy)
    components/chat/agent-response/  # Detail card rendering (opaque text blocks)
    hooks/                   # SSE streaming hook (@agenisea/sse-kit)
    types/                   # API type definitions

docker-compose.yml           # Profile-based: dev, lean (Jaeger), full (Langfuse)

Architecture Insights

For deep-dive architectural decisions, trade-offs, and lessons learned, see ARCHITECTURE_INSIGHTS.md.


Built by Agenisea™ 🪼

About

AI-powered operations assistant for an industrial rental marketplace. Helps ops staff triage issues by querying orders, checking customer sentiment, and finding active rentals using natural language.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages