Most agent frameworks hand one LLM the steering wheel, the pedals, and the rear-view mirror at the same time. VQ Agent Core splits the job into three layers with a locked loop: THINK → ACT → DECIDE → THINK. I want to walk through the code, because the interesting decisions here are architectural, not cosmetic.
The loop
agent.py defines an Agent class. Input goes to THINK (think.py: understand()), which returns an actionable_request that ACT routes on. ACT is needle_router.py — Needle 2 (cactus-needle==2.0.15), grammar-constrained function calling, so tool name + args are structurally valid by construction rather than by hope. If the routing confidence falls below router.confidence_threshold (default 0.7 in config.yaml) or the request is ambiguous, the agent calls _decide_gate() — the DECIDE layer. Then THINK's compose() writes the final answer. Every event lands in runs/run-<timestamp>.jsonl. Full audit trail, one file per run.
THINK degrades honestly
This is my favourite part of think.py. When no local model is reachable:
-
understand()returns the raw request taggedvia="skipped"— logged, not hidden. -
compose()returns a factual structured summary of what actually happened — never synthetic LLM prose.
The media describer inlines text files under 100KB and marks binary media as "described by metadata only" instead of inventing contents. The code comments say it plainly: "Never fakes content." More agent code should have this property.
ACT: the tool registry
Tools live under tools/ with a registry (tools/__init__.py, tools.get(name)). What I found in the tree: local_tools.py (draft_text), system_tools.py (get_time, sysinfo), web_tools.py (web_search, fetch_url), plus notes_tools and decide_tools. Small on purpose — it's a registry meant to be extended, not a kitchen sink. Needle 2 runs in-process via ctypes on desktop, or over HTTP to the needle CLI binary via serve — that's the Termux/Android path, so the same engine runs on a phone.
DECIDE: four judges, one interface
deciders.py is a factory keyed on decider: in config.yaml (jev | local | auto):
-
Jev (TypeSafe cloud) — purpose-built decision model,
choice / score / noulin one call. - Strands Decider 2B (local) — Qwen3.5-2B + pointer head + LoRA, Apache-2.0, ~4.5GB weights, calibrated typed answers in one forward pass.
- Laya (local) — ~1GB purpose-built decision model.
- Prompt-based local LLM — last resort, strict-JSON prompting, uncalibrated but free and offline.
LocalDecider._backends = ("strands", "laya", "prompt") — tried in order, each raising a typed *DeciderUnavailable the runner catches. All four expose the same interface: decide(), classify_reply(), score_lead(), should_escalate(). Swapping Jev for on-device changes no calling code, only the config value. auto = Jev first, local on failure. If nothing works, the error propagates and the agent escalates to a human — safe default, coded.
The concrete jobs: classify_reply() turns a prospect email into e.g. demo_requested with confidence; score_lead() returns temperature + fit 1–5; should_escalate() returns a probability and a recommended action. Tools read the same VQ_DECIDER env var agent.py sets, so standalone tool calls stay consistent with the running agent.
Running it
git clone https://github.com/Shivay00001/vq-agent-core.git
pip install -r requirements.txt
# point config.yaml -> local_model.base_url at an OpenAI-compatible endpoint
# decider: jev | local | auto
python agent.py "score this lead: ..."
fetch_model.sh helps with model downloads; phone-setup.md covers the Termux path (Needle serve backend + prompt-based local decider, since torch won't install there). The repo also ships test_agent.py, test_strands_backend.py, and dated engineering notes from real e2e runs.
Honest take
Working foundation, not a product: no UI, no managed service, THINK's multimodal ability depends on the model behind your endpoint, and the local DECIDE backends want a PC-class machine. But the properties that matter — honest fallbacks, a swappable calibrated judgment layer, full run tracing — are in the code, not the README. Worth a read if you're building agents you need to audit or run offline.
Top comments (0)