SmartShop RAG Product Advisor
A local shopping assistant: hybrid retrieval and cross-encoder reranking over a store’s own catalogue.
A personal full-stack project: a FastAPI and React application that learns a writing style from imported posts, drafts platform-ready posts and replies from it, runs every draft through a guardrail battery, and moves nothing to a queue without an explicit approval. Approved content is scheduled and published exactly once by Celery workers through the platforms’ own OAuth APIs; incoming comments are ingested, threaded and triaged into the same review queue. The create → approve → publish → engage loop is complete and tested end to end against a fake platform adapter — no live account has been connected yet.
SocialAgent drafts social posts in a person’s own voice, schedules them, and drafts replies to the comments that come back — and puts a human decision in front of every one of them.
The approval step is not a setting. A draft moves `draft → approved → queued → published`, and the transition into a queue is the only path to publication, enforced in the data model and asserted by tests. There is no switch that turns it off.
It is code-complete against the MVP scope: voice profiles, drafting, guardrails, approval, scheduling, publishing, comment ingestion, reply drafting, a content calendar, evaluation metrics and a React front end for all of it. What has not happened is a live platform connection — every integration is verified against a fake adapter while the platform app reviews are outstanding, which is why this sits as a prototype rather than something shipped.
Tools that post automatically on someone’s behalf tend to produce content that reads like nobody wrote it, because nobody did. The failure is not occasional — an off-voice post is the normal output, and the person finds out when it is already public.
So the design question was never “how much can be automated?” but “where does the human decision sit, and what stops it being skipped?”. Putting that gate in the schema rather than in a policy is the decision the rest of the system is built around.
Content work is repetitive enough to want help with and personal enough that fully automating it defeats the point — and the systems that do it touch someone’s public identity, their platform accounts and their credentials.
My Role
This is my own project, built end to end: the planning set that preceded the code, the data model and migrations, authentication and the encrypted token vault, the OAuth integrations, voice extraction and draft generation behind a provider abstraction, the guardrail battery, the approval gate, the scheduling and publishing workers, comment ingestion and reply drafting, the evaluation harness and metrics, the React front end, and the CI and quality gates.
Build the loop in the order it runs, and make the human gate a property of the data model rather than a step the code is trusted to call.
A brief or a calendar slot is drafted against a voice profile extracted from imported posts. Every draft — a post, or a reply written from an ingested comment — passes the guardrail battery, then waits in the approval gate, which is the only transition that can queue it. Approved items are scheduled and published exactly once by Celery workers through the platform’s OAuth API, using tokens held in a Fernet-encrypted vault. Approval decisions feed the evaluation metrics.
A style guide extracted from posts the person already wrote, and editable afterwards.
Drafts written to the target platform’s limits and conventions, not one generic post.
Deterministic rules, model-based checks and a prompt-injection scan run before a person is asked to approve.
Approval is the only transition that queues an item, enforced in the schema and asserted by tests.
Scheduled work survives a worker restart: due times live in the database, and a missed slot is caught up or flagged.
Comments are deduplicated, threaded and classified; suggested replies go back through the same gate.
OAuth credentials are encrypted at rest, never logged and never returned by the API.
Edit rate per voice profile and safety pass rate in production, plus an offline golden set that gates prompt changes.
Style imitation is the part most likely to fail in a way that is worse than not trying — a near-miss on someone’s voice reads as inauthentic.
The gate keeps a bad draft private, and the edit rate per voice profile turns “does this sound like me?” into something visible rather than a feeling.
Running the evaluation harness for the first time blocked the release: against the local development model, the safety gate failed on the adversarial cases, and one politely-worded prompt injection was obeyed outright.
The guardrails caught every one of those failures, which is what the layered design predicts — but the run also settled the model question: a small local model is fine to build against and is not a candidate for drafting anything public. No baseline was saved, because a failing run must not become the bar.
Every platform-facing decision — endpoint shapes, payloads, API version pins, what each platform permits an automation to do — is unverifiable until app review clears.
The integrations sit behind one adapter interface with a fake implementation, so the whole loop could be built and tested without waiting — while staying honest that this is the largest untested surface in the project.
| Decision | Alternative considered | Why |
|---|---|---|
| Make approval a state transition in the data model. | A configurable auto-publish mode, or approval enforced in the service layer. | Approval limits how much time the tool can save, and that is the whole point of the trade: the content stays the person’s own. Putting it in the schema means a future feature cannot quietly route around it — the cost is that convenience features like auto-approve rules become deliberate work rather than a flag. |
| Build the entire loop against a fake platform adapter. | Waiting for platform app review before building the integration-dependent half. | Nothing in the schedule waited on a review queue outside the project’s control, and CI stays offline and fast. In exchange, every platform-specific detail is unverified until credentials land, and the acceptance suite has to be re-run against a real sandbox account before any of it counts as working. |
| Run a small local model in development behind a provider abstraction. | Developing directly against the hosted model intended for production. | Local inference costs nothing, keeps sample posts on the machine, and lets the test suite fake the provider entirely. It also flatters nothing: the weaker model is what exposed how much the guardrails were actually carrying. The catch is that quality readings taken in development do not transfer, so the evaluation has to be re-run against the production model before its scores mean anything. |
The full loop works end to end: import writing samples, extract a voice, draft a post or a reply, clear the guardrails, approve it by hand, and watch it publish on schedule — with comments coming back in, triaged, and answered through the same gate. A React front end covers all of it: composer, approval inbox, content calendar and settings.
It is honestly a prototype, not something shipped. Every platform interaction is verified against a fake adapter because the app reviews are outstanding, the evaluation harness has been run once and blocked on the local development model, and no production hosting exists. What that leaves is a complete, tested system with one unverified edge — and a written record of exactly where that edge is.
Screenshots to be added
Have a software, AI, RAG, or web application project in mind? Let’s discuss what you need and determine the right technical approach.