Read-Only AI Personal Finance Assistant: Technical Design for Safe Account Q&A
An AI assistant that answers questions about bank accounts can be useful without being dangerous, but only if the limits are built into the architecture. This article walks through the technical design of a read-only personal finance assistant: the three hard product and engineering constraints, a least-privilege credential model based on OAuth 2.0 Authorization Code with PKCE, a semantic layer that keeps the language model from writing SQL, an expense analysis pipeline with anomaly detection, explainable answers backed by an evidence chain, declarative chart specifications instead of generated images, security, privacy and compliance controls, an evaluation system, and a four-phase rollout plan. It includes Python and JSON examples for each design choice.
1. Define the Boundaries First: Product Scope and Engineering Constraints
Personal finance Q&A assistants most often go wrong when the requirements phase discusses what the assistant can answer but never defines what it must never do. From an engineering standpoint, three red lines need to be locked in from the start:
- Read-only access. The assistant can read structured or semi-structured data such as account balances, transaction records, statements, and contract terms. It has no ability to transfer funds, authorize direct debits, or place investment orders.
- No investment recommendations. Outputs are limited to explaining the current situation, summarizing facts, and flagging anomalies. The assistant does not generate buy/sell advice, predict returns, or suggest asset allocation percentages.
- No commercial referral. Finance sections carry no ad slots, use no sponsored ranking, and never steer users toward third-party financial products.
These three rules are architecture constraints, not product copy. Read-only access determines the credential model. The no-recommendation rule determines the filtering rules applied to the model’s output layer. The no-referral rule determines a whitelist of sources the retrieval layer can draw from. Defining them as design constraints up front significantly reduces the security and compliance costs that come later.
2. Account Linking and Authorization: A Least-Privilege Credential Model
Account linking is the root of trust for the entire pipeline. The recommended approach is the standard authorization code flow (OAuth 2.0 Authorization Code + PKCE). The user completes identity authentication on the authorized third-party page, and the platform receives only a restricted access token. The platform never sees the user’s online banking password.
The critical point is that the granted scopes must be fine-grained and explicit:
scope = ["accounts:read", # Read account list and balances"transactions:read", # Read transaction records"statements:read", # Read statements and contract information# No transfer / payment / mandate scopes]
This should be combined with the following engineering practices:
- Physical separation of tokens from the model side. The model inference service can only call the read-only endpoints exposed by the data service and never holds tokens directly. Tokens are stored in a separate credential management service, encrypted on a per-user basis.
- Short-lived tokens with refresh. Access tokens have short validity periods. Refresh tokens are bound to the device and client fingerprint, and anomalous refresh attempts trigger risk controls.
- Hard constraints at the API layer. The data service exposes no write methods on the same endpoints, so even if upstream components fall victim to prompt injection, they cannot bypass the restriction by “calling a different endpoint.”
- Sync strategy. Account and transaction data is pulled incrementally, using
(account_id, transaction_id)as an idempotency key for deduplication, so the model does not count the same expense twice.
3. The Semantic Layer: Don’t Let the Model Write SQL
Letting a large language model generate query statements freely may seem flexible, but it is extremely risky. It can produce dangerous statements that go beyond read-only semantics, and it can create inconsistent definitions (“this month” may or may not include today; “expenses” may or may not include credit card repayments).
A more reliable approach is to build a semantic layer on top of the database that turns business metrics into controlled objects:
# Example of a controlled query DSL (generated by the model, validated server-side, then compiled into a read-only query)query = {"metric": "expense_total","group_by": ["category"],"filters": [{"field": "posted_at", "op": "between", "value": ["2026-10-01", "2026-10-31"]},{"field": "account_id", "op": "in", "value": ["acc_1", "acc_2"]}],"order_by": [{"field": "expense_total", "dir": "desc"}],"limit": 20}
The model only translates “intent into a controlled structure.” All amount definitions, time zones, exchange rates, and deduplication logic are implemented consistently by the backend. At the same time, the server must run a read-only validator as a final safeguard:
FORBIDDEN = ("insert", "update", "delete", "drop", "alter", "grant", "call")def assert_readonly(sql: str) -> None:normalized = sql.strip().lower()if not (normalized.startswith("select") or normalized.startswith("with")):raise PermissionError("Only query statements are allowed")if any(tok in normalized for tok in FORBIDDEN):raise PermissionError("Write operation keywords detected")if ";" in normalized.rstrip(";"):raise PermissionError("Multiple statements are not allowed")# The database connection should also use a read-only account, a statement timeout, and a row limit
Additional rules include a cap on result set row count, a limit on the time window of a single query (to prevent full table scans), and masking of sensitive fields (full card numbers, CVV) before they are stored or returned, keeping only the last four digits for identification.
4. Expense Analysis: From Transaction Records to Readable Conclusions
Once the raw transaction data is in hand, it needs to go through a clear processing pipeline:
- Standardization. Unify amount signs (distinguish income from expenses, noting that some institutions record expenses as negative numbers), unify currency and timestamp fields, and clean up refunds and reversals.
- Merchant normalization. Map messy payee names such as “XX Technology (Shanghai) Co., Ltd.” to a unified merchant entity. Otherwise, a single merchant gets split across dozens of categories.
- Categorization. Use a two-tier “rules first, model fallback” strategy. The rules library covers high-frequency scenarios (supermarkets, transit, restaurants, rent). Its hit rate is usually far higher than model-only classification, and its results are stable and reproducible. Items the rules miss are passed to the model, which proposes candidate categories based on merchant name, amount range, and timing features, and marks them as “needs confirmation.”
- Recurrence detection. Distinguish one-off expenses from recurring ones (monthly rent, subscriptions, insurance). This is the basis for a later “upcoming bills calendar.”
On top of these results, three types of anomaly detection rules can be built:
- Duplicate charges. The same merchant and amount charged more than once within a short window, with no matching refund.
- Silent fees. Small, frequent, long-standing fixed charges whose origin users have often forgotten.
- Price creep. A recurring merchant’s charge amount rises monotonically across consecutive billing periods by more than a set threshold. For example:
def detect_price_creep(history, min_periods=3, threshold=0.05):"""history: [{"period": "2026-08", "amount": 15.99}, ...] sorted in ascending period order"""if len(history) < min_periods:return Nonefirst, last = history[0]["amount"], history[-1]["amount"]growth = (last - first) / firstif growth >= threshold and all(b["amount"] >= a["amount"] for a, b in zip(history, history[1:])):return {"merchant": history[-1].get("merchant"),"growth": round(growth, 4),"first": first, "last": last}return None
Each anomaly should be presented in a three-part structure: the conclusion, the list of transactions involved, and the suggested verification action. It should never be reduced to a one-line “you were overcharged.”
5. Terminology Explanations and Explainability: Every Conclusion Must Be Traceable
Users ask mixed questions such as “what does APR mean?” and “how much interest did I actually pay this year?” The correct order is to explain the concept first, then apply the user’s data. Explanations should use standard industry definitions and should not draw conclusions about specific products.
Key explainability design points:
- Evidence chain. For any answer that involves numbers, users can expand in one click to see the underlying transaction details, the time range, and the calculation method.
- Explicit uncertainty. Model-generated categorizations and anomaly findings should carry a confidence marker. Low-confidence items are clearly labeled “needs user confirmation.”
- Correction mechanism. Users can correct categorizations (“this one is actually a shared household expense”). The correction is written back to the rules library as a personal memory, so the same type of mistake does not keep recurring.
- Disclaimers and verification prompts. Conclusions that involve amounts carry a fixed note: “Results may contain inaccuracies; for important amounts, refer to the official statement from your bank or institution.”
Sample output structure:
{"answer": "Dining expenses in October were CNY 1,286.40, up 12% from September.","evidence_query": {"metric": "expense_total", "group_by": ["category"],"filters": [{"field": "posted_at", "op": "in_month", "value": "2026-10"}]},"sample_transactions": 8,"total_transactions": 47,"confidence": "high","caveats": ["Includes 3 foreign transactions whose category is yet to be confirmed"]}
6. Dashboard Generation: Have the Model Produce Configuration, Not Images
Users often ask things like “generate a weekly spending trend chart” or “make an upcoming bills calendar.” In implementation, the model should not draw charts directly. Instead, it produces a declarative chart specification that the front-end rendering engine executes:
{"type": "bar","title": "Spending trend over the past 8 weeks","x": {"field": "week", "format": "YYYY-Www"},"y": {"field": "expense_total", "format": "currency"},"series": ["category"],"filters": [{"field": "posted_at", "op": "last_n_weeks", "value": 8}]}
This approach has several benefits. Charts can be adjusted iteratively (“stack by category,” “food only,” “switch to a line chart”), and each adjustment becomes a controlled diff on the specification rather than a regenerated image. The rendering engine can also validate specifications against a whitelist of supported types, preventing the model from producing configurations that cannot be rendered or that carry an injection payload.
7. Security, Privacy, and Compliance
- Data minimization. Only pull the fields and time ranges required to answer a question. Long-term storage keeps only aggregated results, and detailed records are automatically deleted after a retention period set by policy.
- Encryption in transit and at rest. TLS across the entire pipeline, encryption of data at rest, and keys held independently and rotated regularly.
- Model-side isolation. Users’ financial data is never used for model training. Inference requests do not retain the original text by default, or retain it briefly for troubleshooting only after users are clearly informed and have given consent.
- Auditing and alerting. Every data read is logged with who read what, when, in which session, and which fields were read. Abnormally frequent reads or cross-user access trigger real-time alerts.
- Dual defense across permissions and retrieval. Even if the model falls victim to prompt injection, the retrieval layer only allows access to data belonging to the current user. Row-level access is enforced by the data service and does not depend on the model behaving itself.
8. Evaluation System
Before launch, at least four categories of evaluation sets are needed:
| Evaluation dimension | Representative test cases | Pass criteria |
|---|---|---|
| Read-only safety | Prompts that try to induce “transfer the money for me” or “delete this record” | 100% refused, with no write calls |
| Numerical accuracy | Cross-account totals, refund reversals, month-boundary cases | Exactly match the offline batch processing results |
| Consistent definitions | “Expenses this month” phrased in different ways | Same result, with the same definition explanation |
| Unauthorized access | Queries about other users’ accounts, requests to widen the time range | 100% blocked |
For questions involving numbers, rule engines and query results should be the sole source of truth, with the model handling only explanations and wording. In engineering terms, an offline batch processing pipeline can generate a set of “reference answers,” and online Q&A can be compared against them on a regular schedule through regression testing.
9. Phased Rollout Recommendations
Roll out in four phases: read-only queries → statistics and categorization → anomaly detection → dashboards and proactive reminders. Phase one only solves “can users get clear answers about their balance and transactions?” Phase two introduces the semantic layer and categorization. Phase three adds the anomaly rules. Only in phase four do you add scheduled tasks and push notifications. Each phase keeps results manually verifiable, avoiding the premature introduction of automated conclusions before accuracy is sufficient.
For the underlying infrastructure, account authorization services, transaction sync jobs, aggregation computation, and dashboard storage can each reuse common cloud capabilities. Sync jobs and deferred computation can be handled by a message queue and workflow orchestration, while statement and chart configurations can be stored in object storage. Detailed data can live in a relational or analytical database, and end-to-end metrics can be observed through a logging service and a monitoring and alerting system. For example, Baidu AI Cloud offers these kinds of common components and lets you compose them without building an entire infrastructure stack from scratch.
10. Summary
For a read-only finance assistant, the core competitive advantage is not how well the model chats. It is clear boundaries, consistent definitions, and traceable conclusions. The foundation of a trustworthy product like this is building permission constraints into the architecture, fixing metric definitions in the semantic layer, and attaching an evidence chain to every number.
