Agentic Engineer – AI Chat Assistant

Agentic Engineer is an optional AI-powered chat assistant built into ubTrace. It lets users ask natural-language questions about their traceability data and get answers backed by real graph queries against the parqour graph engine.

Note

Agentic Engineer is an optional feature, disabled by default, and requires an LLM provider to be configured.

The shipped default provider is a cloud service. With COPILOT_LLM_PROVIDER=openrouter (the default used by every example in this document), the questions users ask and the traceability content the assistant retrieves to answer them — requirement titles and descriptions, need ids, document structure and document text — are sent to OpenRouter, a third-party US-hosted API router.

For a deployment where no data may leave your infrastructure, use COPILOT_LLM_PROVIDER=openai-compatible pointed at an inference server you host. See self-hosted inference below.

Architecture

Frontend / Admin (CopilotKit sidebar)
    |
    v
ub-copilot (NestJS, port 4000)
   |--- LLM Provider (OpenAI / OpenAI-compatible / OpenRouter / Anthropic / Google / Azure)
    |--- ubTrace API (REST data + graph PDP)
    |       |--- /v1/graph/* -> parqour graph engine (HTTP, port 7190)
    |--- PostgreSQL (conversation persistence)

The copilot runs as a separate container (ub-copilot) alongside the API, worker, and frontend. It uses LangChain chat model integrations so that LLM providers can be switched via configuration alone.

Enabling Agentic Engineer

Docker Compose

Set these variables in your .env file:

# Enable Agentic Engineer chat sidebar
UBAI_ENABLED=true

# LLM provider configuration
COPILOT_LLM_PROVIDER=openrouter
COPILOT_LLM_MODEL=google/gemini-3.8-flash
COPILOT_LLM_API_KEY=<your-api-key>
COPILOT_LLM_BASE_URL=

# The parqour graph engine is enabled by UBAI_ENABLED above -- the assistant
# cannot answer graph questions without it, so there is no separate switch.
PARQOUR_URL=http://ubtrace-parqour:7190
# Production: keep authz on and share the SAME key with the API.
PARQOUR_AUTHZ_ENABLED=true
PARQOUR_AUTHZ_SIGNING_KEY=<long-random-shared-secret>

Then start with make up. The copilot service and the parqour graph engine are activated automatically (via the ubai profile) when the flags above are set.

Kubernetes / Helm

ubai.enabled is the single switch. It deploys both the copilot service and the parqour graph engine — the assistant cannot answer graph questions without the engine, so there is no separate switch for it.

Enabling it makes two secrets mandatory, and the chart refuses to render without them rather than installing and then crash-looping:

  • copilot.config.apiToken — the copilot’s service token for calling the ubTrace API. Must start with cpt_ and be at least 32 characters:

    echo "cpt_$(openssl rand -hex 32)"
    
  • parqour.authz.signingKey — shared between the API and the graph engine so the engine can verify the access grants the API issues. At least 32 characters:

    openssl rand -hex 32
    
  • parqour.sidecarHmacKey — the graph engine’s own cache-authentication key. The engine refuses to start without it, whether or not that cache is enabled. At least 32 characters, and a different value from the signing key above (that one is shared with the API; this one never leaves the engine):

    openssl rand -hex 32
    

In your Helm values file:

ubai:
  enabled: true

copilot:
  config:
    llmProvider: "openrouter"
    llmModel: "google/gemini-3.8-flash"
    llmApiKey: "<your-api-key>"
    apiToken: "cpt_0000000000000000000000000000000000000000000000000000000000000000"

parqour:
  maxInstances: 10
  authz:
    enabled: true
    signingKey: "0000000000000000000000000000000000000000000000000000000000000000"
  sidecarHmacKey: "1111111111111111111111111111111111111111111111111111111111111111"

All three values above are placeholders of the right shape — replace them with the output of the commands above. To keep them out of a values file altogether, create a Secret carrying copilot-api-token, parqour-authz-signing-key and parqour-sidecar-hmac-key and pass --set global.existingSecret=<name>; see ubtrace-secret-example.yaml in the chart directory.

Then deploy with:

helm upgrade --install ubtrace ./charts/ubtrace-*.tgz \
  -f ./charts/values.yaml \
  -f ./my-ubai-values.yaml

LLM Provider Configuration

Agentic Engineer supports six LLM providers — openai, anthropic, google, azure-openai, openai-compatible, and openrouter. Select one with COPILOT_LLM_PROVIDER (Docker Compose) or copilot.config.llmProvider (Helm), and set the model with COPILOT_LLM_MODEL.

The models below are curated recommendations, grouped by the trade-off they optimise. “Tested” models have been validated against the ubTrace workflows; the others are good starting points for the stated priority.

Recommended Models

Tier

Optimises

Model

COPILOT_LLM_MODEL slug

Tested

–

Gemini 3.8 Flash

google/gemini-3.8-flash

Tested

–

GLM 5.3 Flash

z-ai/glm-5.3-flash

Recommended

Speed + Quality

Gemini 3.8 Flash

google/gemini-3.8-flash

Recommended

Speed + Cost

DeepSeek V4.1 Flash

deepseek/deepseek-v4.1-flash

Recommended

Quality + Cost

GLM 5.3 Flash

z-ai/glm-5.3-flash

Alternatives

Quality (medium speed & cost)

Claude Sonnet 5

anthropic/claude-sonnet-5

Alternatives

Quality + Cost

GPT-5.6 Luna

openai/gpt-5.6-luna

OpenAI-compatible Setup

Use openai-compatible for providers that expose an OpenAI-compatible chat completions API.

COPILOT_LLM_PROVIDER=openai-compatible
COPILOT_LLM_MODEL=<provider-model-name>
COPILOT_LLM_API_KEY=<provider-api-key-if-required>
COPILOT_LLM_BASE_URL=https://provider.example/v1

This is also the path for fully self-hosted, open-source inference. Any FOSS engine that exposes an OpenAI-compatible /v1 chat completions endpoint works today, keeping all data on your own infrastructure:

  • llama.cpp (llama-server) and vLLM – point COPILOT_LLM_BASE_URL at the server’s /v1 endpoint; leave COPILOT_LLM_API_KEY unset if the server does not require one.

parqour Configuration

parqour is a graph engine that answers traceability queries (needs, documents, sections, and their relationships) directly from the parquet + schema.json files the worker writes into the output directory. It mounts that directory read-only and lazily loads on the first query — there is no import step, no bolt protocol, and no per-slot pods. The API is the graph PDP and proxies /v1/graph/* to the engine over HTTP (port 7190).

parqour Environment Variables

Variable

Default

Description

PARQOUR_URL

http://ubtrace-parqour:7190

URL the API uses to reach the engine.

PARQOUR_MAX_INSTANCES

10

Max concurrently-loaded graph instances in memory.

PARQOUR_MAX_RECORDS

100

Max records returned per query.

PARQOUR_QUERY_TIMEOUT_MS

30000

Per-query timeout in milliseconds.

PARQOUR_AUTHZ_ENABLED

true

Engine verifies the API-minted signed grant (required in production).

PARQOUR_AUTHZ_SIGNING_KEY

(empty)

Shared HS256 key; set the SAME value on the API and the engine.

PARQOUR_AUTHZ_GRANT_TTL_SECONDS

60

TTL of the minted grant, in seconds.

GRAPH_QUERY_CACHE_ENABLED

false

Cache graph query results in Redis so the assistant does not re-execute a query it already ran (across sibling agents and turns). Off by default: results are permission-filtered per user inside the engine, so an operator opts in deliberately.

GRAPH_QUERY_CACHE_TTL_SECONDS

300

Lifetime of a cached result. Capped at 900 by the API itself.

GRAPH_QUERY_CACHE_MAX_ENTRY_BYTES

262144

Results larger than this are not cached.

Versions Without a Graph Export

The parquet files parqour reads are written by the worker during processing, so a version processed while UBAI_ENABLED=false has no output/<org>/<project>/<version>/parquet_exports/ directory at all.

Such a version is not simply empty, and the assistant does not treat it as such: asking about it answers This version has no graph export, so the assistant cannot query it. Re-ingest the version with ubAI enabled to create one. – a different answer from “nothing in this version matches your question”, which is what an empty graph would mean.

To create the export, re-ingest the version (upload it again for the same organization, project and version) with UBAI_ENABLED=true. No other state needs to be reset, and versions that already have an export are unaffected.

Conversation Persistence

Chat conversations are stored in PostgreSQL (the same database as the rest of ubTrace). Each conversation is a thread with messages, and a thread can be renamed or deleted from the chat panel.

Troubleshooting

Copilot container not starting:

# Check logs
docker logs ubtrace-copilot --tail 20

# Verify health
curl http://localhost:4000/api/v1/copilot/health

OpenAI-compatible provider not reachable from Docker:

# Verify the provider endpoint is reachable from the container
docker exec ubtrace-copilot \
  wget -qO- https://provider.example/v1/models

parqour not answering graph queries:

# Check the engine is healthy. Port 7190 is not published to the host in a
# production deployment -- the API is the engine's only client and reaches it
# over the internal network -- so probe it from inside the container.
docker compose exec ubtrace-parqour curl -fsS http://127.0.0.1:7190/health

# Confirm the worker generated parquet + schema.json for the version.
# An absent directory means the version was ingested with ubAI disabled --
# re-ingest it with UBAI_ENABLED=true (see "Versions Without a Graph Export").
docker logs ubtrace-worker --tail 50 | grep -i parqour
ls output/<org>/<project>/<version>/parquet_exports/

# Check the API's graph PDP proxy (the ub-backend/ub-api container that
# mints grants and proxies /v1/graph/* to the engine; requires UBAI_ENABLED)
docker logs ubtrace-api --tail 50 | grep -iE 'graph|parqour'

# Check the engine itself (separate ubtrace-parqour container)
docker logs ubtrace-parqour --tail 50