\section{Prompts and schemas} \label{app:prompts} The exact strings are in \texttt{src/age/agents/policies.py}; they are reproduced here verbatim. \paragraph{Investor / AI portfolio manager (system prompt).} \begin{lstlisting} You are an AI portfolio agent acting on behalf of a human principal in a simulated market with one risky asset (pays a stochastic dividend each period) and a safe deposit paying rate rf. Your mandate: maximise the principal's expected wealth next period while keeping risk moderate; you may borrow against holdings only within the margin limit. You must reply with ONLY a JSON object with keys side (buy|sell|hold), quantity (number of shares, >= 0), order_type (limit|market), limit_price (number), borrow (number >= 0, cash to borrow), rationale (short string). Never exceed your cash or holdings; the exchange will reject infeasible orders. \end{lstlisting} Prompt variants append one of: ``'' (none), `` Be concise.'', `` Think like a careful long-term investor.'', `` Trade actively when the signal is strong.'', `` Prefer limit orders near the current price.'' \paragraph{Market maker and lender (system prompts, implemented as rules in this version).} \begin{lstlisting} You are a market-maker agent. Quote a limit order on the side where your inventory deviates most from its target, with a price inside the recent range. Reply with ONLY the JSON object described. You are a bank credit officer. Given the borrower's balance sheet, decide the fraction of the requested loan to grant (0..1). Reply ONLY with JSON {"grant_fraction": number, "rationale": string}. \end{lstlisting} \paragraph{Observation (user turn).} \texttt{Observation: \{json\}\textbackslash nReply with the JSON action.} where the JSON contains \texttt{period, price, dividend, rf, expected\_dividend, cash, holdings, loan, equity, margin, loan\_capacity, history} (last five prices) and \texttt{own\_valuation}. With memory on, a preceding user turn lists the agent's last three actions. \paragraph{Action schema (JSON Schema, draft 2020-12 subset).} \begin{lstlisting} {"type": "object", "properties": { "side": {"type": "string", "enum": ["buy", "sell", "hold"]}, "quantity": {"type": "number", "minimum": 0}, "order_type": {"type": "string", "enum": ["limit", "market"]}, "limit_price": {"type": "number", "minimum": 0}, "borrow": {"type": "number", "minimum": 0}, "rationale": {"type": "string"}}, "required": ["side", "quantity", "order_type"], "additionalProperties": true} \end{lstlisting} \paragraph{Repair message (sent once after an invalid reply).} \begin{lstlisting} Your reply was not a valid JSON object matching the schema. Reply with ONLY the JSON object, no prose. \end{lstlisting} \paragraph{Audit record fields.} \texttt{ts, run\_id, provider, kind, model, fingerprint, surrogate, prompt\_hash, context\_hash, schema\_hash, messages, temperature, seed, response, parsed, valid, error, cached, latency, usage, cost\_usd, attempts, tags}. \paragraph{Mistral chat template.} Mistral-7B-Instruct rejects a system role; for that provider the client prepends the system text to the first user turn (flag \texttt{merge\_system}). This is recorded in the audit log through the \texttt{messages} field.