Skip to content
SS
Sunny Sahsi
Back to Projects
Featured Projectin progress

Enterprise AI Chatbot & Agent Orchestrator

A Go backend service that lets operational teams ask questions in plain language and get accurate answers pulled from 600+ microservice APIs by an LLM.

🏛️
Deployment Status: Staging Verified (Parked in Office Politics Limbo)Fully engineered, tested, and battle-proven in staging. Deployment to production is currently delayed due to classic corporate roadmap politics, steering committee debates, and OKR reshuffling. Architecture patterns and generic open-source templates shared below!
Timeline2024
Visibilitypublic after review
Artifacts
Private
GoLLM APIsRESTCursorClaude
Project screenshot
1. The Problem & Context

Why this project was initiated

The system sits on top of a multi-service backend (Service 1 for configuration, Service 2 for business operations, Service 3 for reporting, plus an auth service). The agent turns a natural-language question into the right API calls, filters tenant data strictly in code, and returns a verified structured answer. Note: Code is battle-tested in staging; production deployment is currently awaiting clearance from corporate steering committee politics.

2. Technical Implementation & Highlights

Engineering Deliverables

Built the chatbot backend service on top of an existing multi-service system.
Ran an AI-assisted audit to find implementation issues, then fixed them with manual verification.
Handled data-accuracy problems such as fixed reporting windows and missing user names in session data.
Drafted a phased plan for agent routines with approvals.

Deep Dive: Agent Architecture & Tool Orchestration

Production Status (The Real Talk): Architected, built, and thoroughly battle-tested in staging. However, it is currently parked in legendary corporate deployment limbo due to classic enterprise office politics & roadmap reshuffling (yes, the code works; no, the steering committee hasn't agreed on who clicks "Deploy" or whose quarterly OKR gets the glory). Status: Fully functional Go orchestrator, awaiting bureaucratic clearance. A generalized open-source template and architectural blueprint are shared below!

Project: Enterprise AI Assistant and Tool Layer for Multi-Service Platforms Role: Backend / platform engineering — orchestrator, tool registry, security gates, automation hooks Stack: Go, PostgreSQL, OpenAPI-derived tools, multi-provider LLM adapter, OAuth/MCP (read path), scheduled routines

In enterprise operations, business and operational teams need answers immediately—whether reconciling yesterday’s accounts, verifying entity statuses, or checking operational records across distributed systems. A single “chat API” cannot answer those questions safely: data lives behind several REST microservices, permissions differ by role, and “today” on the wall clock is not today in the accounting books.

This project is an agent orchestrator that sits between staff (and, on a separate surface, external AI clients) and downstream services. It does not own ledgers or entity records directly. It plans reads, executes only allowlisted GETs under the caller’s credentials, forces tenant scope in code, and turns JSON into structured answers—with a fallback path when the model is unavailable.


Multi-Service Topology

The enterprise backend is composed of discrete services:

Service DomainResponsibility
Auth & SessionJWT validation, tenant membership, RBAC route matrix
Service 1 (Settings)Type definitions, configurations, rates, operational metadata
Service 2 (Operations)Core domain entities, workflows, operational lifecycles
Service 3 (Analytics)Aggregated summaries, collections, export-oriented reads
diagram
[ Staff question in plain text (+ active tenant) ]
                        ↓
[ Agent Orchestrator (Go) — single binary ]
   ├── Session gate: token valid + user allowed on tenant
   ├── Business-date resolution (reconciliation anchor, not server clock)
   ├── Intent / planner path OR keyword handler fallback
   ├── Tool allowlist (OpenAPI-generated reads, admin/export/internal stripped)
   ├── Tenant injection on every downstream call (model cannot widen scope)
   └── Parallel GET execution → user JWT forwarded as downstream identity header
                        ↓
[ Internal REST APIs — RBAC enforced per route ]
                        ↓
[ Presenter: short text + typed cards (table, chart, stats) ]
                        ↓
[ Verified answer + persisted turn & tool audit ]

Parallel tracks (same security philosophy, different front door):
   • Scheduled / conditional routines — grant + short-lived delegated token at run time
   • MCP read-only tool server — OAuth bearer, no in-house planner, same read filter

Design choice: Treat the LLM as a planner and presenter, not as the system of record. Numbers come from API responses; scope and dates are server invariants.


Critical Problem: “Today,” Time Zones, and Missing Query Params

When queries used relative language—_“yesterday,” “last week,” “which accounts renewed today”_—naive LLM-orchestrated API calls failed in predictable ways:

  • •The model assumed UTC or server local time, not the organization’s operational business date.
  • •OpenAPI tools often require paired date fields or explicit filters; omitted params yielded empty reports or 400s the model could not recover from.
  • •Letting the model freely set tenantId would violate one-tenant-at-a-time isolation rules.

Resolution: A three-layer contract—

LayerBehavior
Anchor contextResolve “today” from the business operating date (cached briefly from Service 1 configuration), with timezone for display and scheduling.
DispatcherBefore any outbound GET: overwrite path/query tenantId (and tenant path segments) with the active tenant; fill required dates from business date or copy partner fields (startDate ↔ endDate).
Planner policyModel chooses _which_ allowlisted tools to call; it does not own tenant scope or the authoritative calendar.

Illustrative orchestration (portfolio pseudocode—not production):

internal/orchestrator/request_context.go
package orchestrator

import (
	"context"
	"time"
)

type ReadRequest struct {
	TenantID      string
	UserToken     string
	BusinessDate  time.Time // reconciliation "today"
	LocationTZ    *time.Location
	PlannedReads  []PlannedToolCall
}

func (e *Engine) ExecuteReads(ctx context.Context, req ReadRequest) ([]ReadResult, error) {
	for i := range req.PlannedReads {
		call := &req.PlannedReads[i]
		if !e.allowlist.IsPermittedRead(call.ToolName) {
			return nil, ErrToolNotInPolicy
		}
		// Invariant: scope and dates are server-owned
		call.Args = e.forceTenantScope(call.Args, req.TenantID)
		call.Args = e.fillRequiredDates(call.Args, req.BusinessDate, req.LocationTZ)
	}
	return e.runParallelGETs(ctx, req.UserToken, req.PlannedReads)
}

Outcome: Relative questions map to the same windows operational teams expect on reports, without trusting the model to guess ISO boundaries or tenant ids.


Critical Problem: Security Without Prompt Religion

Interactive chat benefits from prompts (“refuse writes,” “mask card numbers,” narrow scope). External MCP clients bring their own models and prompts—so MCP and automation paths rely only on:

  • •OAuth consent with an explicit tenant on the grant
  • •Opaque access token → token exchange → ~120s delegated user JWT used once per downstream call
  • •Same read allowlist as chat; no write tools in v1 MCP
  • •Separate audit rows attributing user, client, grant, tool, and outcome

Lesson for the portfolio: Split “AI product surface” (planner, cards, confirmations) from “AI tool surface” (protocol + allowlist + injection). Shared filters; not shared trust in instructions.


Tool Orchestration Model

Hundreds of generated tools are registered from sibling OpenAPI specs. At runtime:

diagram
Registry (all generated tools)
        ↓ filter at startup
Allowlisted GET reads only (~300+ tools, minus admin/internal/export)
        ↓ planner or MCP tools/list
Subset chosen per turn OR full list exposed to MCP client
        ↓ dispatcher
Forced tenantId + date/param completion → parallel execution
        ↓
Presenter (chat) OR passthrough JSON (MCP)

Multi-step operational workflows (approvals, entity assignment, status changes) use an agent loop: reads until context is sufficient, then in-chat confirmation flows or schema-driven write forms—writes never bypass the user’s token and downstream RBAC. Background routines reuse the same engine with a reviewed definition, grant-based tokens, and an allowlisted autonomous write path where product requires it.


What I Learned

  • •Enterprise agents are state machines with guardrails. The LLM proposes tool names and parameters; invariants (tenant scope, business date, allowlist, compliance patterns) belong in Go.
  • •OpenAPI as the tool contract scales better than hand-written integrations—if paired with a strict filter and CI regen from service specs.
  • •One permission system. The orchestrator does not maintain a shadow RBAC matrix; 403 from upstream becomes a denied or error card—simpler audits, sharper UX when roles are wrong.
  • •Two HTTP personalities in one process (chat streaming vs MCP streams) avoid timeout and trust-boundary mistakes.
  • •Automation credentials are grants, not service accounts. Background runs exchange revocable secrets for short-lived tokens with live permission re-check—same pattern as MCP, different UX.

What’s Next (and what shipped adjacent to this work)

PhaseCapability
Shipped / in treeScheduled and conditional routines (Postgres queue, workers, inbox/chat/email delivery); MCP read-only tool server with OAuth
NextWebhook- and event-driven routine triggers with debounced condition evaluation; richer MCP capability kinds (guarded writes, notifications) with re-consent
ProductHuman approval for high-risk autonomous writes in automation; multi-replica session state for MCP tenant switching

Architectural Highlights

  • •Built a Go orchestrator between a Next.js workspace panel and multiple microservices, with streaming SSE and card-based answers.
  • •Solved business-date and timezone anchoring so relative user language maps to correct report windows.
  • •Designed MCP + OAuth token exchange so external AI tools inherit the same read allowlist and RBAC as the in-app chat agent—without forwarding long-lived browser JWTs.
  • •Introduced routines as validated JSON pipelines with grant-based background execution and audit parity with interactive chat.

Architecture Flow (Mermaid)

Architecture Diagram
Rendering diagram...

Engineering Outcome & Takeaways

Shipping this project reinforced the value of clean architectural boundaries, automated verification loops, and clear error telemetry. In subsequent iterations, I focus on system observability, end-to-end integration test suites, and strict performance profiling under load.