An AI-first customer support platform. Frontline answers customer questions from your own documentation, escalates to a human when it should, and gives you the audit trail to see why every answer came out the way it did.
Go backend, Next.js dashboard, pluggable vector store, and a widget you can drop onto any site.
Above: the embedded widget. The assistant answers a product question by grounding it in an indexed service manual, keeping the steps and safety notes intact.
Answers from your knowledge, not from guesswork. Documents are ingested, chunked and indexed into a vector store, then retrieved at answer time. Every retrieval is logged, so you can replay what the model actually saw.
Decides when a human should take over. Each conversation runs in one of three service modes — AI only, human only, or AI-first with human takeover. When the AI hands off, the dispatcher picks an agent by live schedule, availability, current load and priority, and falls back to a team or global waiting pool when nobody is free.
Handles the work around the conversation. Tickets, customers and companies, tags, quick replies, team schedules, and a full RBAC layer over users, roles and permissions.
Meets customers where they are. One conversation model behind a web widget, WeCom customer service, WeChat Official Account, Telegram, Zalo OA, and Discord.
Shows its work. Agent runs and workflow runs are recorded with step counts, tool success, latency, token usage, handoff rate and knowledge-fallback rate.
Collections of documents with per-document index state, directory organization, and a retrieval log tab for inspecting what was returned for a given query.
Each agent carries a model configuration, a service mode, a skill set and an optional set of MCP tools. Configuration is versioned — agents are published as numbered revisions and can be rolled back.
Aggregate quality metrics across runs, plus a per-run table with duration, token split and errors. Runs are filterable by conversation, agent and status.
| Layer | Technology |
|---|---|
| Backend | Go 1.26, Gin, GORM |
| AI runtime | CloudWeGo Eino — graph-based tools with human-in-the-loop interrupts |
| Frontend | Next.js, exported as a static SPA and served by the Go binary |
| Workflow editor | Vendored Flowgram editor (flowgram-editor/) |
| Relational store | MySQL 8.4 |
| Vector store | Qdrant (default) or LanceDB |
The AI runtime exposes capabilities as graph tools rather than plain function calls, so multi-step flows can pause for customer confirmation and resume later. Handoff and ticket creation both use this: the customer is asked to confirm before anything happens.
Request flow follows handler → service → repository. GORM stays in the repository, transaction boundaries sit in the service, and response assembly happens in builders.
Requires Docker and Docker Compose.
git clone --recurse-submodules <your-repo-url> frontline
cd frontline
docker compose up --buildThe dashboard is then at http://localhost:8083.
This starts three services: frontline (port 8083), mysql 8.4, and qdrant
(ports 6333/6334). Configuration is mounted from docker/frontline.yaml.
Two alternative stacks are available:
docker compose -f docker-compose.lancedb.yml up --build # LanceDB instead of Qdrant
docker compose -f docker-compose.sqlite-lancedb.yml up --build # SQLite + LanceDB, no MySQLCopy config/config.example.yaml and edit it, or override any value with a
FRONTLINE_-prefixed environment variable (FRONTLINE_SERVER_PORT, and so on).
Configuration blocks worth knowing about:
server— port, instance branding, and the CORS allowlist. The allowlist is empty by default, which rejects cross-origin browser requests; add your embedding site's origin before going live.db— MySQL connection.vectorDB—qdrantorlancedb. LanceDB needs the backend built with-tags lancedbplus the native libraries; usetask build:lancedb.auth/customerSession— console JWT settings, and the signing secret and TTL for customer session tokens.customerSession.secretmust be set or customer sessions fail to issue.storage— attachment and avatar storage.mcp— global switch for MCP tool integration.oidc,wxWork— single sign-on and WeCom credentials.
The platform is English-only: language is en-US and that is the only supported value.
<script>
window.FrontlineConfig = {
channelId: "your-channel-id",
externalId: "your-user-id", // omit to let the widget mint a guest id
externalName: "Ada Lovelace",
}
</script>
<script async src="https://your-host/sdk/frontline-sdk.min.js"></script>A channel serves any number of customers. Identity comes from externalId — each distinct
value is a distinct customer with its own conversation, so you do not need a channel per
user. Leave it out and the widget generates a guest id and keeps it in localStorage.
For authenticated users, supply getUserToken() instead and verify it server-side against
the channel's token secret.
Go and pnpm are needed locally.
task dev # backend and frontend dev servers
task build # frontend SPA plus a Go binary into dist/
go test ./...
cd web && pnpm buildBackend enums are the source of truth and are generated into
web/lib/generated/enums.ts via task enums. Never hand-write a frontend duplicate.
Routes are registered explicitly in internal/bootstrap/routes.go — handler method names
do not create routes.
cmd/ entry points
internal/
ai/runtime/ graph tools, workflow executor, agent runtime
handlers/ api (public), dashboard (console), third (channel webhooks)
services/ business logic, dispatch and handoff
repositories/ GORM data access
models/ GORM models
pkg/enums/ enum definitions, generated to the frontend
web/ Next.js dashboard and widget SDK
flowgram-editor/ vendored workflow editor
qdrant/ vector store submodule
docker/ per-stack runtime configuration
Apache License 2.0. See LICENSE.



