From Chatbot to Product Copilot: How We Productionized Zorgax in MyZubster
Over the last development cycle, we moved Zorgax from “AI chat inside the product” to a much more complete product copilot architecture.
The interesting part was not connecting an LLM.
The interesting part was everything around it:
routing
- billing
- fallbacks
- grounding
- observability
- UX
- product navigation
This post is a walkthrough of what we implemented in production.
- Hybrid routing: OpenAI for complex tasks, local AI for the rest
Zorgax now uses a hybrid AI architecture.
The backend classifies requests broadly into:
standard
complex
Signals like:
GitHub
Vercel
deploy
debug
code
research
LIFE
KPI
MRV
automation
can push a request into the complex tier.
Research mode and very long prompts can also trigger the complex route.
Conceptually:
const tier =
useResearch ||
complexSignalCount >= 2 ||
text.length >= 2500
? "complex"
: "standard";
The routing logic then looks roughly like:
STANDARD
↓
Ollama / general AI gateway
COMPLEX / RESEARCH
↓
OpenAI
This avoids sending every trivial prompt to the remote model.
- OpenAI runtime enablement
One production issue was especially useful.
The OpenAI API key was already configured in Vercel, but Zorgax was still answering through Ollama.
The runtime logs showed:
aiProvider: ollama
aiModel: qwen2.5:3b
aiFallbackReason: astra_disabled
So the problem was not the API integration itself.
It was the runtime gate.
We changed the logic so that OpenAI is considered available when:
OPENAI_API_KEY is configured
AND
ZORGAX_ASTRA_KILL_SWITCH != true
This means the API key is the primary source of truth, while we keep a dedicated emergency kill switch.
The production status endpoint now exposes safe state:
{
"openai_configured": true,
"astra_enabled": true,
"astra_kill_switch": false,
"astra_model": "gpt-5.6-sol"
}
No secret is exposed.
- The first real OpenAI request failed with HTTP 429
Once routing was correctly enabled, we hit the next production problem:
OpenAI HTTP 429
Initially that only told us “something is wrong”.
That was not enough.
A 429 can mean different things:
temporary rate limit
credit exhausted
project spend limit
organization usage limit
So we upgraded the error handling.
Now Zorgax extracts and logs safe metadata like:
status
error code
error type
request id
without logging the API key or secret payloads.
- Precise fallback reasons
Instead of exposing only:
openai_error
we now classify errors more precisely.
For example:
openai_credit_balance_exhausted
openai_rate_limit
openai_rate_limit_exceeded
openai_project_spend_limit_exceeded
openai_organization_usage_limit_exceeded
This immediately improved debugging.
In our real production test, the system returned:
fallback: openai_credit_balance_exhausted
That told us the code path was working correctly and the remaining problem was simply billing.
After adding API credit, the next request succeeded.
- Retry + secondary OpenAI model
For transient 429s, we added controlled retry behavior.
The backend now:
- calls the primary OpenAI model
- retries once if the error is transient
- respects Retry-After when reasonable
- optionally tries a second OpenAI model
- falls back to Ollama if necessary
The current primary model is:
gpt-5.6-sol
and the secondary OpenAI fallback defaults to:
gpt-5.6-luna
This is different from the final Ollama fallback.
So the real chain can be:
gpt-5.6-sol
↓
retry
↓
gpt-5.6-luna
↓
Ollama
- Budget-aware OpenAI usage
We also kept the application-side monthly budget guard.
Before a remote request is sent, the system:
checks current monthly spend
estimates request cost
reserves budget
calls OpenAI
records actual usage
settles the reservation
We improved this too.
The budget is now shared across OpenAI fallback models instead of being split by model.
We also record the actual OpenAI model used.
That matters because if a request starts with gpt-5.6-sol but completes with gpt-5.6-luna, the usage record should reflect reality.
- Model-specific cost estimation
We also made cost estimation model-aware.
Instead of assuming one fixed pricing profile, the router now uses different pricing assumptions for different OpenAI models.
Conceptually:
const MODEL_PRICING_USD_PER_M = {
"gpt-5.6-sol": {
input: 4,
output: 20
},
"gpt-5.6-terra": {
input: 2,
output: 12
},
"gpt-5.6-luna": {
input: 0.2,
output: 1.2
}
};
The goal is not just cost accounting.
It is better routing discipline.
- Reducing TPM pressure
We also reduced the default maximum OpenAI output allowance from:
4096 tokens
to:
2048 tokens
while keeping it configurable.
That helps reduce:
token pressure
latency
cost
rate-limit risk
for the kind of product guidance Zorgax usually generates.
- Showing the actual provider in the UI
One of the most useful changes was extremely simple.
The backend already returns:
{
"ai_provider": "openai",
"ai_model": "gpt-5.6-sol",
"ai_fallback_reason": null
}
The frontend now displays it directly below the answer:
AI: OpenAI · gpt-5.6-sol
or:
AI: Ollama · qwen2.5:3b · fallback: openai_credit_balance_exhausted
This removes ambiguity.
The user does not have to trust that the intended model was used.
They can see it.
- Vercel observability
The server also logs routing metadata for each successful Zorgax request.
For example:
event: zorgax_message_sent
plan: pro
webResearch: true
sourceCount: 0
aiProvider: openai
aiModel: gpt-5.6-sol
aiFallbackReason: null
This is what let us verify the production request end-to-end.
Not:
“I think OpenAI handled it.”
But:
“This exact production request used OpenAI and had no fallback.”
That is a much better operational standard.
- Grounding: the next major problem
Once OpenAI was working correctly, we immediately found another issue.
The model became too conservative.
It answered that almost everything in MyZubster was unverified.
Why?
Because the OpenAI path and the Ollama path were receiving different runtime context.
The local/general AI route had explicit product facts.
The OpenAI route did not.
That created an asymmetry:
OLLAMA
system prompt
- runtime product facts
- user prompt
OPENAI
system prompt
- user prompt
So we fixed the architecture instead of weakening the grounding rules.
- One shared canonical runtime context
Now both AI paths receive the same canonical product facts.
We introduced a shared runtime context roughly like this:
RUNTIME PRODUCT FACTS
- myzubster.com is live
- /marketplace is a live route
- Seller starts from Marketplace
- /social-login is the auth route
- /metaverse is a live route
- /life-pilot is a live/pilot route
- MYZ is internal accounting/reward
- advanced roadmap features remain unverified unless supported by evidence
This block is injected into both:
OpenAI prompts
Ollama/general AI prompts
and is also used when estimating OpenAI input cost.
That guarantees prompt consistency.
- LIVE route does not mean every feature is verified
This distinction became central.
A page can be live without every possible feature being live.
For example:
/metaverse exists
does not prove:
virtual Seller showrooms exist
Likewise:
/life-pilot exists
does not automatically prove:
photo + GPS environmental reporting
scientific validation
public map publishing
So Zorgax now explicitly reasons with categories like:
LIVE / IMPLEMENTED
PILOT / EXPERIMENTAL
PROPOSED / PLANNED
UNKNOWN / UNVERIFIED
The goal is to avoid both extremes:
hallucinating features
and:
denying real implemented routes
- A production test after the grounding fix
After sharing the canonical runtime context, we tested Zorgax with:
Analyze the current state of MyZubster. Tell me which functions are LIVE/IMPLEMENTED, which are PILOT/EXPERIMENTAL, which are PROPOSED/PLANNED, and which are NOT VERIFIED. Do not invent anything.
This time the answer correctly identified:
LIVE
- Marketplace
- Seller flow
- /social-login
- /metaverse
- /life-pilot route
- open-source contribution flow
- MYZ internal accounting
PILOT
- LIFE Pilot activities
UNVERIFIED
- advanced Seller dashboards
- advanced Metaverse capabilities
- user-facing IPFS/IPNS flows
- IoT / robotics integrations
- external settlement layers
- LIFE photo+GPS reporting
And the UI showed:
AI: OpenAI · gpt-5.6-sol
The runtime confirmed:
aiProvider: openai
aiModel: gpt-5.6-sol
aiFallbackReason: null
That was the first complete end-to-end success.
- Fixing the chat UX
We also spent time on something less glamorous but very important: the chat itself.
Originally:
the conversation area was too small
long answers opened at the bottom
users had to scroll upward to start reading
We changed the behavior so that:
the chat viewport is larger
responses begin from the top
new assistant messages scroll to their beginning
the input focus does not override scroll position
The important UX logic is roughly:
function scrollMessageToTop(element) {
const delta =
element.getBoundingClientRect().top -
messages.getBoundingClientRect().top;
messages.scrollTo({
top: messages.scrollTop + delta - 12,
behavior: "smooth"
});
}
and:
input.focus({ preventScroll: true });
This made long AI responses much easier to read.
- Guided navigation
Zorgax is not meant to be an isolated AI page.
It is part of MyZubster.
So we also improved the guided navigation.
The page now exposes quick paths such as:
Seller
Marketplace
Metaverse
LIFE Pilot
University / research
Community
Party planning
We also fixed spacing and wrapping in the Vai direttamente section so CTA labels do not visually collide.
- Home and Marketplace navigation
One final UX problem remained.
Once a user entered Zorgax, it was not obvious how to return to the main product.
We added persistent navigation directly in the Zorgax header:
🏠 Home MyZubster
🛒 Marketplace
These remain visible while using Zorgax.
On mobile they move to their own row.
We also track the Home navigation as part of the existing Zorgax funnel analytics.
This sounds small, but it changes the page from:
standalone AI destination
into:
a real part of the MyZubster product navigation
- The current architecture
At this point the flow looks like this:
USER
↓
Zorgax UI
↓
Access + policy
↓
Web research
↓
Task classification
↓
Shared canonical runtime context
↓
AI router
├── OpenAI
│ ├── gpt-5.6-sol
│ ├── retry
│ └── gpt-5.6-luna
│
└── Ollama fallback
↓
Grounded answer
↓
Provider / model metadata
↓
Vercel observability
↓
User
- What the project taught us
The LLM call was still the easiest part.
The real AI product is:
model
- router
- cost guard
- retries
- fallbacks
- product truth
- permissions
- logs
- UX
- navigation
The more capable the model becomes, the more important it is for the application to define:
what is real
what is live
what is experimental
what is allowed
what is verified
what actually happened
That is the direction we are taking with Zorgax.
Not:
let AI control the product.
But:
let AI reason inside a product whose runtime state, permissions, evidence and execution remain explicit.
Current production status
Today, Zorgax in MyZubster has:
OpenAI routing ✅
Ollama fallback ✅
gpt-5.6-sol production path ✅
secondary OpenAI fallback ✅
budget guard ✅
429 diagnostics ✅
billing working ✅
provider/model visibility ✅
shared grounding context ✅
LIVE / PILOT / UNKNOWN separation ✅
larger chat UX ✅
better scrolling ✅
guided navigation ✅
Home navigation ✅
Marketplace navigation ✅
Vercel observability ✅
And this is now running on the live MyZubster deployment.
We did not come to conquer. We came to build together.
Per DEV userei questo titolo:
From Chatbot to Product Copilot: How We Productionized Zorgax with OpenAI, Ollama, Grounding and Runtime Routing
Top comments (0)