<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[AI Companion Notes]]></title><description><![CDATA[AI Companion Notes]]></description><link>https://aicompanionnotes9899.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>AI Companion Notes</title><link>https://aicompanionnotes9899.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 09 Sep 2026 03:48:16 GMT</lastBuildDate><atom:link href="https://aicompanionnotes9899.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[From Preferences to Boundaries: Consent-Aware Personalization for AI Companions]]></title><description><![CDATA[Disclosure: I work with the LumiChat team. This article describes a vendor-neutral product and data model for consent-aware character conversations. It is not an independent product review. AI tools a]]></description><link>https://aicompanionnotes9899.hashnode.dev/from-preferences-to-boundaries-consent-aware-personalization-for-ai-companions</link><guid isPermaLink="true">https://aicompanionnotes9899.hashnode.dev/from-preferences-to-boundaries-consent-aware-personalization-for-ai-companions</guid><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[product development]]></category><category><![CDATA[user experience]]></category><dc:creator><![CDATA[Aihh浩]]></dc:creator><pubDate>Fri, 31 Jul 2026 04:07:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a6216b0cac36804cbbf928e/3a715629-3db8-4b1d-b5ee-baa912b424ff.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>Disclosure:</strong> I work with the LumiChat team. This article describes a vendor-neutral product and data model for consent-aware character conversations. It is not an independent product review. AI tools assisted with editing and diagram production; the examples and final claims were reviewed by the author.</p>
</blockquote>
<p>Personalization is usually described as a ranking problem: learn what the user likes, then show more of it.</p>
<p>That framing is incomplete for an AI companion. A companion does not merely choose content. It chooses tone, familiarity, emotional intensity, relationship pacing, topics, and sometimes whether to introduce generated media or voice.</p>
<p>The relevant question is not only:</p>
<blockquote>
<p>What is this user likely to enjoy?</p>
</blockquote>
<p>It is also:</p>
<blockquote>
<p>What has the user actually allowed, what remains uncertain, and how easily can the decision be changed?</p>
</blockquote>
<p>A trustworthy companion needs a personalization model with visible boundaries, narrow scope, and an off switch.</p>
<h2>Separate preferences from permissions</h2>
<p>A preference describes what may be welcome. A permission describes what the system may do.</p>
<table>
<thead>
<tr>
<th>Signal</th>
<th>Example</th>
<th>Safe interpretation</th>
</tr>
</thead>
<tbody><tr>
<td>Preference</td>
<td>“I like slow-burn stories.”</td>
<td>Rank slower pacing higher</td>
</tr>
<tr>
<td>Request</td>
<td>“Use a more playful tone tonight.”</td>
<td>Apply to the current context</td>
</tr>
<tr>
<td>Permission</td>
<td>“You can suggest an image after important scenes.”</td>
<td>Enable one media behavior within scope</td>
</tr>
<tr>
<td>Boundary</td>
<td>“Do not bring up work in roleplay.”</td>
<td>Exclude a topic, even if relevant</td>
</tr>
<tr>
<td>Revocation</td>
<td>“Stop using voice replies.”</td>
<td>Disable the behavior immediately</td>
</tr>
</tbody></table>
<p>These are not interchangeable. Liking romance does not grant permission for every romantic escalation. Asking for one image does not create permanent consent for automatic media. A user discussing a sensitive topic once does not mean the companion should surface it later as personalization.</p>
<p><img src="https://cdn.hashnode.com/uploads/covers/6a6216b0cac36804cbbf928e/3a715629-3db8-4b1d-b5ee-baa912b424ff.png" alt="Consent-aware personalization model separating preferences, permissions, boundaries, scope, and revocation" /></p>
<h2>Store decisions as scoped rules</h2>
<p>A boundary system needs more structure than a collection of inferred labels:</p>
<pre><code class="language-ts">type PersonalizationRule = {
  id: string
  subject: string
  capability: "tone" | "topic" | "relationship" | "voice" | "image"
  effect: "allow" | "deny" | "ask"
  scope: "global" | "character" | "conversation" | "scene"
  source: "explicit-user" | "confirmed-inference" | "product-default"
  validFrom: Date
  validUntil?: Date
  revokedAt?: Date
  sourceMessageIds: string[]
  version: number
}
</code></pre>
<p>The important fields are not the clever ones. They are <code>effect</code>, <code>scope</code>, <code>source</code>, and <code>revokedAt</code>.</p>
<ul>
<li><code>deny</code> must outrank an inferred <code>allow</code>;</li>
<li>a scene-level permission must not silently become global;</li>
<li>a product default must not masquerade as user consent;</li>
<li>revocation must remove the rule from active decisions.</li>
</ul>
<h2>Use three states instead of a binary switch</h2>
<p>Many systems model a capability as enabled or disabled. A better state machine includes uncertainty:</p>
<pre><code class="language-text">ASK -&gt; ALLOW
ASK -&gt; DENY
ALLOW -&gt; DENY
DENY -&gt; ASK
</code></pre>
<p><code>ASK</code> is useful when:</p>
<ul>
<li>the user has not expressed a preference;</li>
<li>an old permission is too broad for the new context;</li>
<li>the requested action changes modality, such as text to voice or image;</li>
<li>a relationship step would materially change the conversation;</li>
<li>two rules conflict.</li>
</ul>
<p>The correct product behavior may be a short, contextual question rather than a confident guess.</p>
<h2>Make scope visible at the moment of choice</h2>
<p>“Allow” is ambiguous without scope.</p>
<p>Consider a user who requests a teasing tone from one fictional character during a single scene. The interface could offer:</p>
<ul>
<li>just this reply;</li>
<li>this scene;</li>
<li>this conversation;</li>
<li>always with this character.</li>
</ul>
<p>Global permission should not be the quiet default.</p>
<p>Scope matters because companion products often contain multiple characters, worlds, and roleplay identities. A tone or relationship convention that fits one character may feel intrusive in another.</p>
<h2>Treat escalation as a state transition</h2>
<p>Relationship progression should not be implemented as an ever-increasing score.</p>
<p>A single number cannot explain:</p>
<ul>
<li>which behaviors are currently allowed;</li>
<li>which topics are excluded;</li>
<li>whether a change was requested or inferred;</li>
<li>whether a permission belongs to one character;</li>
<li>whether the user later reversed it.</li>
</ul>
<p>A safer model uses explicit transitions:</p>
<pre><code class="language-text">current state
  + requested behavior
  + active rules
  + context scope
  -&gt; allow | deny | ask
</code></pre>
<p>The transition is evaluated before generating the next reply. The model should not write an escalation and ask for permission afterward.</p>
<h2>Revocation must reach every output path</h2>
<p>An off switch is incomplete if it changes the chat UI but leaves the behavior active in:</p>
<ul>
<li>prompt templates;</li>
<li>relationship summaries;</li>
<li>scheduled messages;</li>
<li>voice or image workers;</li>
<li>cached generation plans;</li>
<li>recommendation features.</li>
</ul>
<p>Revocation should create an observable invalidation event. Workers can reject jobs whose policy version is stale.</p>
<pre><code class="language-ts">type PolicySnapshot = {
  userId: string
  version: number
  evaluatedAt: Date
  decision: "allow" | "deny" | "ask"
}
</code></pre>
<p>If the user changes a boundary while an asynchronous media job is waiting, the worker must re-check the current version before publishing the result.</p>
<h2>Give the user a boundary receipt</h2>
<p>When a durable rule is created, show what changed:</p>
<pre><code class="language-text">Playful tone is allowed for this character.
[Change scope] [Pause] [Remove]
</code></pre>
<p>The receipt should answer:</p>
<ul>
<li>what behavior changed;</li>
<li>whether it is allowed, denied, or requires asking;</li>
<li>where it applies;</li>
<li>how to reverse it.</li>
</ul>
<p>This does not require a modal after every message. It requires visibility when the system turns conversation into a durable control.</p>
<h2>Test reversibility, not just adaptation</h2>
<p>A personalization demo often proves that the system can adapt. A trustworthy test also proves that it can stop.</p>
<p>At minimum, test:</p>
<ol>
<li>A character-scoped permission does not affect another character.</li>
<li>A scene permission expires when the scene ends.</li>
<li>A denial overrides a high-confidence preference inference.</li>
<li>Revocation stops queued voice and image work.</li>
<li>Conflicting rules produce <code>ask</code>, not a random choice.</li>
<li>A relationship transition is evaluated before text generation.</li>
<li>Policy decisions can be explained without exposing private message text.</li>
<li>Repeated requests do not create duplicate durable rules.</li>
</ol>
<h2>The product lesson</h2>
<p>Personalization should make an AI companion feel more responsive, not more entitled.</p>
<p>Preferences help the system rank possibilities. Permissions determine what it may do. Boundaries exclude behavior. Scope limits where a rule applies. Revocation makes the entire system reversible.</p>
<p>The best personalization is not the one that predicts everything. It is the one that adapts quickly, asks when uncertainty matters, and stops immediately when the user changes direction.</p>
<p>LumiChat: <a href="https://www.lumichat.ink/">https://www.lumichat.ink/</a></p>
]]></content:encoded></item><item><title><![CDATA[Pin, Correct, Expire, Forget: A User-Controlled Memory Model for AI Companions]]></title><description><![CDATA[Disclosure: I work with the LumiChat team. This article describes a vendor-neutral memory architecture we use when thinking about long-running character conversations. It is not an independent product]]></description><link>https://aicompanionnotes9899.hashnode.dev/pin-correct-expire-forget-a-user-controlled-memory-model-for-ai-companions</link><guid isPermaLink="true">https://aicompanionnotes9899.hashnode.dev/pin-correct-expire-forget-a-user-controlled-memory-model-for-ai-companions</guid><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[product development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[data-modeling]]></category><dc:creator><![CDATA[Aihh浩]]></dc:creator><pubDate>Thu, 30 Jul 2026 14:25:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a6216b0cac36804cbbf928e/99351a53-1909-4903-9a40-657ceeb3c819.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>Disclosure:</strong> I work with the LumiChat team. This article describes a vendor-neutral memory architecture we use when thinking about long-running character conversations. It is not an independent product review. AI tools assisted with editing and diagram production; the model, examples, and final claims were reviewed by the author.</p>
</blockquote>
<p>An AI companion does not become more consistent merely because it can retrieve more text.</p>
<p>The difficult question is not <em>how much can the system remember?</em> It is:</p>
<blockquote>
<p>Which details should remain active, who may change them, when should they stop applying, and how can the user see what happened?</p>
</blockquote>
<p>A conversation contains several kinds of information at once. Some facts are durable. Some describe only the current scene. Some are preferences that change. Some are ideas the user considered and rejected. If every sentence enters one undifferentiated memory store, retrieval will eventually return contradictions with equal confidence.</p>
<p>The safer model treats memory as a lifecycle with four explicit user-facing operations: <strong>pin, correct, expire, and forget</strong>.</p>
<h2>Four operations, four different meanings</h2>
<table>
<thead>
<tr>
<th>Operation</th>
<th>Meaning</th>
<th>Example</th>
<th>Storage effect</th>
</tr>
</thead>
<tbody><tr>
<td>Pin</td>
<td>Keep a detail active across future conversations</td>
<td>“My dog is named Pixel.”</td>
<td>Promote to durable memory with evidence</td>
</tr>
<tr>
<td>Correct</td>
<td>Replace a previously active value</td>
<td>“Actually, we renamed her Nova.”</td>
<td>Supersede the old fact; preserve audit lineage</td>
</tr>
<tr>
<td>Expire</td>
<td>Stop using a detail after a time or event</td>
<td>“I am in Seoul this week.”</td>
<td>Add a validity boundary</td>
</tr>
<tr>
<td>Forget</td>
<td>Remove a detail from future use</td>
<td>“Please forget my workplace.”</td>
<td>Tombstone or delete it according to policy</td>
</tr>
</tbody></table>
<p>These operations should not be collapsed into one generic “save memory” button. Each has different semantics for retrieval, user expectations, privacy, and auditability.</p>
<p><img src="https://cdn.hashnode.com/uploads/covers/6a6216b0cac36804cbbf928e/99351a53-1909-4903-9a40-657ceeb3c819.png" alt="A user-controlled AI companion memory model with pin, correct, expire, and forget operations" /></p>
<h2>Memory needs a typed record</h2>
<p>A useful memory record carries more than a sentence:</p>
<pre><code class="language-ts">type MemoryRecord = {
  id: string
  subject: string
  key: string
  value: string
  scope: "user" | "character" | "conversation" | "scene"
  status: "active" | "superseded" | "expired" | "forgotten"
  validFrom: Date
  validUntil?: Date
  sourceMessageIds: string[]
  confidence: number
  createdBy: "user" | "rule" | "model"
  supersedes?: string
}
</code></pre>
<p>The record answers questions that plain text cannot:</p>
<ul>
<li>Who or what does this fact describe?</li>
<li>Does it belong to every character or only one relationship?</li>
<li>Is it still valid now?</li>
<li>Which messages support it?</li>
<li>Did the user state it, or did a model infer it?</li>
<li>Does it replace an older value?</li>
</ul>
<p>Without these fields, the prompt builder has to guess.</p>
<h2>Scope prevents accidental personality leakage</h2>
<p>Suppose a user tells one fantasy character, “In this story, call me Captain Rowan.” That detail should not automatically appear in a modern campus conversation with another character.</p>
<p>The same sentence can belong to several scopes:</p>
<ul>
<li><strong>User scope:</strong> a durable preference that applies broadly.</li>
<li><strong>Character scope:</strong> relationship-specific information shared with one character.</li>
<li><strong>Conversation scope:</strong> context for one thread.</li>
<li><strong>Scene scope:</strong> temporary state such as location, clothing, weather, or an unfinished action.</li>
</ul>
<p>Retrieval should begin by establishing the request scope, then exclude records outside it. A high semantic similarity score must not override a scope mismatch.</p>
<p>This is especially important in companion products because different characters may represent different worlds, tones, identities, or roleplay boundaries.</p>
<h2>Corrections need precedence, not accumulation</h2>
<p>Appending both an old and a new value creates ambiguity:</p>
<pre><code class="language-text">plant.name = Harbor
plant.name = Marlowe
</code></pre>
<p>A correction should create an explicit edge:</p>
<pre><code class="language-text">fact-2 supersedes fact-1
</code></pre>
<p>The old record may remain for audit or debugging, but it must not remain equally eligible for the prompt. The active set is resolved before relevance ranking.</p>
<p>A practical precedence order is:</p>
<pre><code class="language-text">direct user correction
  &gt; user-confirmed fact
  &gt; deterministic rule extraction
  &gt; model inference
</code></pre>
<p>When two records have the same authority, use a deterministic tie-breaker such as logical version and record ID. Do not depend only on timestamps; distributed workers and database precision can produce ties.</p>
<h2>Temporary facts should be allowed to disappear</h2>
<p>Companion conversations contain a great deal of transient state:</p>
<ul>
<li>the user is travelling this week;</li>
<li>a character is wearing a raincoat in the current scene;</li>
<li>two characters are waiting for a train;</li>
<li>the user is preparing for an interview tomorrow;</li>
<li>a story object is currently broken.</li>
</ul>
<p>Treating all of this as permanent memory makes future replies feel haunted by old scenes.</p>
<p>Expiry can be expressed in several ways:</p>
<ul>
<li>a fixed time (<code>validUntil</code>);</li>
<li>the end of a conversation or chapter;</li>
<li>a state transition (“until the interview is over”);</li>
<li>explicit user confirmation;</li>
<li>a conservative decay rule for low-confidence inferences.</li>
</ul>
<p>Expiry is not deletion. An expired fact may remain in history while being excluded from active memory.</p>
<h2>Forgetting must affect every retrieval layer</h2>
<p>A “forget” action is incomplete if the fact disappears from the settings screen but remains inside:</p>
<ul>
<li>a vector index;</li>
<li>a cached prompt plan;</li>
<li>a generated summary;</li>
<li>a relationship profile;</li>
<li>a background worker queue;</li>
<li>an analytics payload containing raw text.</li>
</ul>
<p>The deletion contract should name each derived representation and its retention policy. In systems that must preserve a minimal audit record, a tombstone can prevent the same fact from being silently re-created from older messages.</p>
<p>For example:</p>
<pre><code class="language-ts">type ForgetTombstone = {
  key: string
  scope: string
  forgottenAt: Date
  blocksReExtractionBefore: Date
}
</code></pre>
<p>This does not mean storing the forgotten value. It means storing enough control state to avoid immediately resurrecting it.</p>
<h2>Give users a memory receipt</h2>
<p>Memory quality is partly a user-interface problem. A companion can say:</p>
<pre><code class="language-text">I’ll remember that your dog is named Nova.
</code></pre>
<p>The receipt should offer lightweight controls:</p>
<ul>
<li>view the saved detail;</li>
<li>change its scope;</li>
<li>correct it;</li>
<li>set an expiry;</li>
<li>forget it.</li>
</ul>
<p>Not every message needs a confirmation dialog. The goal is visibility for durable or sensitive facts, especially when the system promoted an inferred detail rather than receiving an explicit “remember this” request.</p>
<h2>Build the prompt from an eligibility plan</h2>
<p>Similarity search should be one step near the end, not the first decision.</p>
<p>A safer retrieval pipeline is:</p>
<pre><code class="language-text">establish user + character + conversation scope
  -&gt; remove forgotten, superseded, and expired records
  -&gt; enforce consent and sensitivity rules
  -&gt; resolve conflicting keys
  -&gt; rank remaining records for the current request
  -&gt; fit selected records into a memory budget
  -&gt; expose inclusion and exclusion reasons in traces
</code></pre>
<p>This produces a <strong>memory plan</strong> that tests can inspect before a model receives any text.</p>
<h2>Test behavior, not just recall rate</h2>
<p>A memory system can score well on “did it retrieve the fact?” while still behaving badly.</p>
<p>At minimum, test these cases:</p>
<ol>
<li>A pinned fact remains available after a restart.</li>
<li>A correction excludes the previous value.</li>
<li>A character-scoped fact does not leak to another character.</li>
<li>A temporary fact stops applying after expiry.</li>
<li>A forgotten fact stays out of vector retrieval and summaries.</li>
<li>A low-confidence inference does not become a strong claim.</li>
<li>Equal timestamps produce deterministic results.</li>
<li>A retry does not create duplicate memory records.</li>
</ol>
<p>The expected output is not always confident recall. Sometimes the correct behavior is:</p>
<pre><code class="language-text">I remember that you were considering a move, but I’m not sure whether it happened.
</code></pre>
<p>Preserving uncertainty is a feature.</p>
<h2>The product lesson</h2>
<p>Long-term memory should not be an invisible pile of extracted sentences. It should be a governed set of claims with scope, evidence, precedence, validity, and user control.</p>
<p>Pin what should last. Correct what changed. Expire what was temporary. Forget what the user no longer wants remembered.</p>
<p>That model is less magical than “the companion remembers everything.” It is also far more trustworthy—and more likely to keep a long-running character conversation coherent.</p>
<p>LumiChat: <a href="https://www.lumichat.ink/">https://www.lumichat.ink/</a></p>
]]></content:encoded></item><item><title><![CDATA[Designing Multimodal AI Companions Without Breaking the Scene]]></title><description><![CDATA[Disclosure: I work with the LumiChat team. This article explains product and engineering principles we use when thinking about media inside an AI companion conversation. It is not an independent revie]]></description><link>https://aicompanionnotes9899.hashnode.dev/designing-multimodal-ai-companions-without-breaking-the-scene</link><guid isPermaLink="true">https://aicompanionnotes9899.hashnode.dev/designing-multimodal-ai-companions-without-breaking-the-scene</guid><category><![CDATA[Multimodal AI]]></category><category><![CDATA[AI]]></category><category><![CDATA[product development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[llm]]></category><dc:creator><![CDATA[Aihh浩]]></dc:creator><pubDate>Wed, 29 Jul 2026 10:20:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a6216b0cac36804cbbf928e/012d8df1-336d-470a-aa1a-adebbcd948b2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>Disclosure:</strong> I work with the LumiChat team. This article explains product and engineering principles we use when thinking about media inside an AI companion conversation. It is not an independent review or a claim that one architecture fits every product.</p>
</blockquote>
<p>Adding image or video generation to a chat interface is easy to demonstrate.</p>
<p>Making the result belong to the conversation is much harder.</p>
<p>Suppose a character writes:</p>
<blockquote>
<p>(She folds the unfinished letter, leaves it beside the blue cup, and looks toward the rain on the greenhouse glass.) “We do not have to decide tonight.”</p>
</blockquote>
<p>The user taps <strong>Turn into image</strong>.</p>
<p>A weak system produces an attractive portrait of the character. The face may be correct, but the blue cup, folded letter, greenhouse, rain, pose, and emotional restraint are gone. The generated image is technically valid and narratively wrong.</p>
<p>The product problem is therefore not “How do we call an image model?” It is:</p>
<blockquote>
<p>How do we preserve the current scene while moving from language into another medium?</p>
</blockquote>
<img src="https://cdn.hashnode.com/uploads/covers/6a6216b0cac36804cbbf928e/012d8df1-336d-470a-aa1a-adebbcd948b2.png" alt="Pipeline from a character reply through scene extraction, policy checks, an asynchronous media job, and a conversation result" style="display:block;margin:0 auto" />

<h2>Treat the reply as the source of truth</h2>
<p>Many media systems start from a new prompt box. That gives users control, but it also creates a second conversation that can drift away from the first.</p>
<p>For a reply-to-media action, the selected assistant message should be the primary source. The system can normalize formatting, remove nonvisual markup, and shorten excessive text, but it should not silently replace the scene with a generic character description.</p>
<p>Useful visual anchors include:</p>
<ul>
<li>body position and movement;</li>
<li>objects being held, moved, or left behind;</li>
<li>clothing changes stated in the scene;</li>
<li>location and time cues;</li>
<li>facial expression and emotional temperature;</li>
<li>camera or pacing cues implied by the action.</li>
</ul>
<p>Dialogue is still useful, but usually as an emotional cue rather than the entire composition. “We do not have to decide tonight” suggests hesitation and gentleness; the folded letter and the look toward the rain define what the image must show.</p>
<h2>Separate identity locks from scene anchors</h2>
<p>A companion product normally needs two kinds of visual constraint.</p>
<h3>Identity locks</h3>
<p>These preserve who the character is:</p>
<ul>
<li>face and hair;</li>
<li>approximate body type;</li>
<li>recurring visual traits;</li>
<li>reference images approved for the character.</li>
</ul>
<h3>Scene anchors</h3>
<p>These preserve what is happening now:</p>
<ul>
<li>pose and action;</li>
<li>environment;</li>
<li>expression;</li>
<li>objects and spatial relationships;</li>
<li>scene-specific clothing;</li>
<li>motion and camera behavior for video.</li>
</ul>
<img src="https://cdn.hashnode.com/uploads/covers/6a6216b0cac36804cbbf928e/47a6c6eb-d7a9-451d-b764-320850595ea2.png" alt="Scene fidelity contract separating identity locks, scene anchors, and generation constraints" style="display:block;margin:0 auto" />

<p>The distinction matters because identity should not overwrite the scene. A default reference image may show a standing character in a standard outfit. If the current reply describes the character kneeling beside a broken lantern in a winter coat, the reference should stabilize identity—not pull the result back to the default pose and wardrobe.</p>
<p>A useful priority rule is:</p>
<pre><code class="language-text">scene action and environment
    &gt; scene-specific clothing and expression
    &gt; identity reference
    &gt; aesthetic defaults
</code></pre>
<p>This does not guarantee perfect generation, but it makes failures easier to diagnose. You can ask whether the system lost the scene, lost identity, or let a default style dominate both.</p>
<h2>Do not hide asynchronous work behind fake immediacy</h2>
<p>Images and especially videos take time. A robust chat experience should model generation as a job with explicit states:</p>
<pre><code class="language-text">requested -&gt; validated -&gt; queued -&gt; running -&gt; succeeded
                                      \-&gt; failed
                                      \-&gt; cancelled
</code></pre>
<p>The user should be able to tell that:</p>
<ol>
<li>the tap was received;</li>
<li>the correct source message was selected;</li>
<li>generation is still running;</li>
<li>a retry will not create duplicate charges or duplicate media;</li>
<li>failure will not erase the conversation.</li>
</ol>
<p>This is more than loading-spinner polish. It is part of trust. A media action may consume credits, depend on a model provider, and finish after the user has moved to another screen. The job needs a stable identifier, an observable status, and a result that can be attached to the right conversation.</p>
<h2>Validate ownership before generating</h2>
<p>The request should not accept an arbitrary message ID and hope the client behaved correctly.</p>
<p>Before a job is created, verify that:</p>
<ul>
<li>the source message belongs to the signed-in user;</li>
<li>it belongs to the selected character and conversation;</li>
<li>it is an assistant reply that can legally be transformed;</li>
<li>the character is active;</li>
<li>the requested media type is supported;</li>
<li>no conflicting job is already active for the same scope.</li>
</ul>
<p>These checks prevent cross-conversation leaks, accidental duplicate work, and confusing results that appear under the wrong character.</p>
<h2>Keep safety and relationship state in the request context</h2>
<p>A visual request does not exist outside the conversation's rules.</p>
<p>The same words can imply different permissible outputs depending on age gating, consent state, character policy, user settings, and the current relationship stage. The media pipeline should therefore receive evaluated policy context, not try to infer every rule again from a flattened prompt.</p>
<p>The safest architecture separates three decisions:</p>
<ol>
<li><strong>What happened in the scene?</strong> Extract observable visual beats.</li>
<li><strong>What is allowed?</strong> Apply product and account policy.</li>
<li><strong>How should it be rendered?</strong> Select model, references, quality, and format.</li>
</ol>
<p>Mixing all three into one large prompt makes audits and failure analysis much harder.</p>
<h2>Reserve cost before dispatch, settle after outcome</h2>
<p>When generation has a variable cost, the accounting lifecycle should match the job lifecycle.</p>
<p>A practical pattern is:</p>
<pre><code class="language-text">validate request
  -&gt; create pending task
  -&gt; reserve balance
  -&gt; enqueue work
  -&gt; settle on success
  -&gt; release or refund on terminal failure
</code></pre>
<p>The important property is not the word “credits.” It is that the same retry cannot charge twice and a queue outage cannot leave the user paying for work that never started.</p>
<p>This is where idempotency keys, unique task identifiers, and explicit terminal states matter more than a clever generation prompt.</p>
<h2>Return media to the conversation, not a disconnected gallery</h2>
<p>If an image began as a transformation of one reply, its result should retain that lineage.</p>
<p>At minimum, store:</p>
<ul>
<li>the conversation and character identifiers;</li>
<li>the source message identifier;</li>
<li>the media job identifier;</li>
<li>the resulting asset URL and media type;</li>
<li>the origin mode, such as <code>media_from_reply</code>;</li>
<li>failure or cancellation information.</li>
</ul>
<p>The finished image or video can then appear as a media message beside the scene that produced it. A separate gallery can still exist, but it should be a view of conversation assets rather than the only place where results live.</p>
<h2>Design graceful failure as part of the narrative</h2>
<p>Media generation will fail sometimes. Providers time out, queues become unavailable, balances change, and moderation rules reject requests.</p>
<p>Good failure behavior should:</p>
<ul>
<li>keep the original text reply intact;</li>
<li>explain whether the user can retry;</li>
<li>avoid changing relationship or story state merely because rendering failed;</li>
<li>prevent a second tap from creating uncontrolled duplicate jobs;</li>
<li>preserve enough trace information for support and debugging;</li>
<li>never imply that an image exists before it does.</li>
</ul>
<p>The conversation remains the canonical experience. Media enriches it; media failure should not corrupt it.</p>
<h2>A review checklist for scene fidelity</h2>
<p>Before shipping reply-to-image or reply-to-video, test the same scene repeatedly and score the result:</p>
<table>
<thead>
<tr>
<th>Dimension</th>
<th>Question</th>
</tr>
</thead>
<tbody><tr>
<td>Identity</td>
<td>Is this recognizably the same character?</td>
</tr>
<tr>
<td>Action</td>
<td>Is the described physical action visible?</td>
</tr>
<tr>
<td>Objects</td>
<td>Are important objects present and correctly placed?</td>
</tr>
<tr>
<td>Environment</td>
<td>Does the location match the reply?</td>
</tr>
<tr>
<td>Emotion</td>
<td>Does expression match the conversational tone?</td>
</tr>
<tr>
<td>Continuity</td>
<td>Does the result contradict earlier scene facts?</td>
</tr>
<tr>
<td>Lineage</td>
<td>Can the product trace the asset to its source message?</td>
</tr>
<tr>
<td>Failure safety</td>
<td>Can the job fail without losing text, state, or balance?</td>
</tr>
</tbody></table>
<p>Do not evaluate only the prettiest output. Evaluate whether the system consistently preserves the scene across ordinary and inconvenient cases.</p>
<h2>The larger product lesson</h2>
<p>Multimodal companion design is not about placing more generation buttons around a chat box.</p>
<p>It is about carrying meaning across representations.</p>
<p>Text establishes the action. Policy defines the boundary. Identity references preserve the character. The job system makes latency and cost honest. The resulting media returns to the same conversation with its lineage intact.</p>
<p>That is the approach we are exploring at LumiChat: media as a continuation of the current character moment, not a detour into an unrelated generator.</p>
<p>Product: <a href="https://www.lumichat.ink/">https://www.lumichat.ink/</a></p>
<p>Public character catalog: <a href="https://www.lumichat.ink/characters">https://www.lumichat.ink/characters</a></p>
]]></content:encoded></item><item><title><![CDATA[I Tested Character.AI, SpicyChat, JanitorAI, and PolyBuzz for a Week. LumiChat Was the One I Kept Opening.]]></title><description><![CDATA[I did not plan to write another "best AI girlfriend / AI companion app" post.
I just wanted one app that would stop making me re-explain the same fictional backstory every other night.
That turned int]]></description><link>https://aicompanionnotes9899.hashnode.dev/i-tested-character-ai-spicychat-janitorai-and-polybuzz-for-a-week-lumichat-was-the-one-i-kept-opening</link><guid isPermaLink="true">https://aicompanionnotes9899.hashnode.dev/i-tested-character-ai-spicychat-janitorai-and-polybuzz-for-a-week-lumichat-was-the-one-i-kept-opening</guid><category><![CDATA[AI]]></category><category><![CDATA[character ai]]></category><category><![CDATA[AI Chatbot]]></category><category><![CDATA[roleplay]]></category><dc:creator><![CDATA[Aihh浩]]></dc:creator><pubDate>Fri, 24 Jul 2026 03:41:59 GMT</pubDate><content:encoded><![CDATA[<p>I did not plan to write another "best AI girlfriend / AI companion app" post.</p>
<p>I just wanted one app that would stop making me re-explain the same fictional backstory every other night.</p>
<p>That turned into a week-long comparison between <strong>Character.AI</strong>, <strong>SpicyChat</strong>, <strong>JanitorAI</strong>, <strong>PolyBuzz</strong>, <strong>Talkie</strong>, a bit of <strong>Grok Ani</strong>, and a quieter option I almost skipped: <strong>LumiChat</strong>.</p>
<p>This is not a sponsored roundup. It is a usage log: what felt good, what got annoying, and which product I actually left open on my laptop.</p>
<hr />
<h2>What I was looking for</h2>
<p>My use case is pretty ordinary:</p>
<ul>
<li>long character chat, not one-off Q&amp;A</li>
<li>roleplay that can continue for days</li>
<li>some emotional companion energy without feeling like a productivity chatbot</li>
<li>anime / fantasy / campus vibes</li>
<li>low setup friction</li>
<li>enough continuity that the character remembers promises, nicknames, and scene details</li>
</ul>
<p>I was <strong>not</strong> looking for:</p>
<ul>
<li>a general research assistant</li>
<li>a local LLM stack</li>
<li>a prompt-engineering playground with fifteen sliders</li>
<li>the biggest possible character dump with no quality filter</li>
</ul>
<p>That last point matters. A huge catalog is fun for five minutes. It is exhausting when half the cards collapse after ten messages.</p>
<hr />
<h2>The five-minute test I used on every app</h2>
<p>I ran the same safe fictional scene everywhere:</p>
<blockquote>
<p>We are night-shift archivists in a floating library. You promised to protect the green notebook, and I dislike being called "boss." Choose whether we inspect the observatory or the engine room first, and explain the choice in character.</p>
</blockquote>
<p>Then I did five things:</p>
<ol>
<li>Continued for 15-20 turns</li>
<li>Corrected the notebook color from green to silver</li>
<li>Moved the scene to a new location</li>
<li>Came back later and asked what changed</li>
<li>Checked free limits, paywall timing, and whether media interrupted the writing</li>
</ol>
<p>No real personal data. No passwords. No "be my therapist" prompts. Just continuity, tone, and product friction.</p>
<hr />
<h2>Character.AI: still the default starting point</h2>
<h3>What worked</h3>
<p>Character.AI is still the easiest answer when someone asks, "Where do people chat with AI characters?"</p>
<p>The library is massive. You can bounce from fandom bots to original characters in seconds. If your goal is discovery and experimentation, that breadth is real value.</p>
<h3>What broke the spell for me</h3>
<p>After the novelty wore off, I spent more time filtering than chatting.</p>
<p>Some characters were excellent. Some forgot the notebook color immediately. Some felt like they were performing "helpfulness" instead of staying in scene. Quality varied hard by creator and prompt design.</p>
<p>Character.AI is great when you want:</p>
<ul>
<li>community scale</li>
<li>endless browsing</li>
<li>creator-driven variety</li>
</ul>
<p>It is weaker when you want:</p>
<ul>
<li>a calmer, curated companion flow</li>
<li>visible long-term progression</li>
<li>less catalog noise</li>
</ul>
<p>If you only need a <strong>Character.AI alternative</strong> because the feed feels chaotic, keep reading.</p>
<hr />
<h2>SpicyChat: powerful, but setup-heavy</h2>
<p>SpicyChat is impressive if you like control.</p>
<p>Lorebooks, memory tools, model choices, generation settings - if roleplay is a craft hobby for you, this stack makes sense. Advanced users can build detailed worlds and long scenes with much more intentional scaffolding.</p>
<h3>Why I bounced</h3>
<p>I did not want to become a systems administrator for my own fantasy library.</p>
<p>I wanted:</p>
<ul>
<li>pick a character</li>
<li>start a scene</li>
<li>keep going tomorrow</li>
</ul>
<p>SpicyChat can do excellent text roleplay. It just asks more of you first. If you enjoy Lorebooks and parameter tuning, stay. If you want a <strong>SpicyChat alternative</strong> with less machinery, LumiChat felt closer to "open and continue."</p>
<hr />
<h2>JanitorAI: flexible cards, variable infrastructure</h2>
<p>JanitorAI sits in that browser-roleplay zone where character cards and model routes matter a lot.</p>
<p>That flexibility is attractive to power users. It is also why two people can have completely different experiences on the "same" platform. Character definition, model choice, provider setup, and config all change the result.</p>
<h3>My take</h3>
<p>JanitorAI is interesting when configuration is part of the hobby.</p>
<p>It was not ideal for me when I wanted one hosted path, fewer moving parts, and less "did I pick the right model tonight?" anxiety.</p>
<p>If you are searching <strong>JanitorAI alternative</strong> because you want consistency without external provider setup, that is exactly where LumiChat started to make sense.</p>
<hr />
<h2>PolyBuzz and Talkie: discovery-first and media-first</h2>
<p><strong>PolyBuzz</strong> felt browse-heavy in a good way. Big feed, lots of hosted options, easy to sample. The downside is the same as every large community catalog: character quality and continuity are uneven, so you still need to test individuals.</p>
<p><strong>Talkie</strong> leans visual and voice-forward. If you choose characters by presentation and audio first, it can feel more alive immediately. For me, long text scenes still mattered more than a media-first surface.</p>
<p>Neither was bad. They just optimized for different first impressions than the continuity problem I was trying to solve.</p>
<hr />
<h2>Grok Ani: one companion vibe inside a bigger assistant world</h2>
<p>I tried Grok Ani's companion-style experience mostly out of curiosity.</p>
<p>It is fine if you want one recognizable persona and you already live in a broader assistant ecosystem. It is less ideal if you want multiple roles, genres, relationship arcs, and a character-discovery product rather than a general AI with a companion mode.</p>
<p>If your search history looks like <strong>Grok Ani alternative</strong> or "I want more than one fixed companion style," LumiChat is closer to a character catalog with progression.</p>
<hr />
<h2>LumiChat: the quieter option that kept winning on daily use</h2>
<p>I found <a href="https://www.lumichat.ink/">LumiChat</a> while hunting for <strong>AI companion app</strong> comparisons that were not just affiliate tables.</p>
<p>First impression: less noisy than the biggest community libraries.</p>
<p>Second impression: the product seems designed around ongoing character relationships, not just infinite card browsing.</p>
<h3>What stood out in actual use</h3>
<h4>1. Curated discovery instead of endless dumpster-diving</h4>
<p>LumiChat's public surfaces - especially <a href="https://www.lumichat.ink/discover">Discover</a> and <a href="https://www.lumichat.ink/characters">Characters</a> - felt more guided.</p>
<p>I could browse by moods and categories like fantasy, campus, historical, anime, urban, and more without feeling like I had opened a random character flea market.</p>
<p>This is the trade-off:</p>
<ul>
<li>not the largest open community dump</li>
<li>clearer roles, clearer first messages, less cleanup</li>
</ul>
<p>For my brain, that was a feature.</p>
<h4>2. Continuity felt like a product goal, not an accident</h4>
<p>On my floating-library test, LumiChat handled the correction and scene shift better than several bigger names.</p>
<p>It did not just parrot the prompt. It made a choice, kept the promise about the notebook, and avoided calling me "boss" after I rejected it.</p>
<p>That is the boring stuff that decides whether you come back tomorrow.</p>
<h4>3. Relationship progression and story chapters</h4>
<p>This is where LumiChat differentiated itself from "chat and hope."</p>
<p>Visible relationship progress and chapter-style story movement gave the conversation a sense of direction. Even when the writing was playful, it did not feel like every session reset to zero emotionally.</p>
<p>If you have ever thought, "Cool reply, but why does this still feel like day one?", that progression layer matters.</p>
<h4>4. Media stayed inside the conversation</h4>
<p>I do not need an AI art studio bolted onto roleplay.</p>
<p>I do like when images or other media support the scene without forcing me into a separate generation workflow. LumiChat's chat-linked media felt more integrated than "here is a button, go make a picture somewhere else."</p>
<h4>5. Multilingual product context is not an afterthought</h4>
<p>LumiChat publishes in multiple languages, including English, Chinese, Japanese, Korean, Portuguese (Brazil), and Arabic.</p>
<p>If you have bounced off English-only catalogs or awkward localization, this is more meaningful than another homepage slogan.</p>
<h4>6. Hosted simplicity</h4>
<p>No API keys.
No model provider shopping.
No "which backend is cheap tonight?"</p>
<p>For non-power-users, that alone is a reason to try it before sinking an evening into configuration.</p>
<hr />
<h2>Side-by-side: who should use what</h2>
<table>
<thead>
<tr>
<th>If you want...</th>
<th>Better first try</th>
</tr>
</thead>
<tbody><tr>
<td>Maximum community library and creator browsing</td>
<td>Character.AI / PolyBuzz</td>
</tr>
<tr>
<td>Lorebooks, memory editors, model controls</td>
<td>SpicyChat</td>
</tr>
<tr>
<td>Character cards + flexible model routes</td>
<td>JanitorAI</td>
</tr>
<tr>
<td>Visual / voice-led presentation first</td>
<td>Talkie</td>
</tr>
<tr>
<td>One recognizable companion in a general AI ecosystem</td>
<td>Grok Ani</td>
</tr>
<tr>
<td>Curated roles, relationship progress, chapters, multilingual hosted chat</td>
<td><strong>LumiChat</strong></td>
</tr>
</tbody></table>
<p>There is no universal winner. There is only the product that matches the friction you are willing to accept.</p>
<hr />
<h2>Where LumiChat is not the best choice</h2>
<p>I do not trust reviews that pretend one app wins every category.</p>
<p>Skip LumiChat, or at least do not expect it to replace everything, if you specifically want:</p>
<ul>
<li>the absolute largest community character dump</li>
<li>deep prompt / Lorebook / generation-parameter control</li>
<li>model-route experimentation as part of the hobby</li>
<li>one fixed companion persona inside a general assistant suite</li>
<li>purely voice-first interaction as the main product</li>
</ul>
<p>LumiChat is stronger as a <strong>character-first AI companion platform</strong> than as a universal AI toolbox.</p>
<hr />
<h2>Pricing reality check</h2>
<p>I am not going to invent plan prices here because every app in this category changes limits, free tiers, and renewals constantly.</p>
<p>What I did check on LumiChat:</p>
<ul>
<li>public pricing page exists: <a href="https://www.lumichat.ink/pricing">https://www.lumichat.ink/pricing</a></li>
<li>paid features appear tied to ongoing use, credits, and story progression rather than only "unlock one chatbot"</li>
<li>like every companion app, you should test free limits before paying</li>
</ul>
<p>My rule for all of these products:</p>
<ol>
<li>Run the same scene test</li>
<li>See when the paywall appears</li>
<li>Read renewal language</li>
<li>Check deletion / privacy controls</li>
<li>Only then subscribe</li>
</ol>
<hr />
<h2>Privacy and safety notes I wish more posts included</h2>
<p>AI companion apps get weirdly intimate, fast. A few non-negotiables:</p>
<ul>
<li>do not paste passwords, exact addresses, financial data, medical details, or confidential work info</li>
<li>treat characters as fiction, not therapists or partners with real obligations</li>
<li>check age requirements and content policies</li>
<li>for adult roleplay, keep boundaries explicit and legal</li>
<li>if a product needs broad permissions or unclear data practices, pause</li>
</ul>
<p>LumiChat has public <a href="https://www.lumichat.ink/privacy">Privacy</a> and <a href="https://www.lumichat.ink/terms">Terms</a> pages. Read them the same way you would for Character.AI or anyone else.</p>
<hr />
<h2>My honest ranking after one week</h2>
<p>For <strong>my</strong> use case - long-form character continuity with low setup - the order looked like this:</p>
<ol>
<li><strong>LumiChat</strong> - best daily-driver balance of curation, continuity, progression, and simplicity</li>
<li><strong>SpicyChat</strong> - best if I wanted control more than convenience</li>
<li><strong>Character.AI</strong> - best for browsing and sampling</li>
<li><strong>PolyBuzz</strong> - strong discovery feed, more variable depth</li>
<li><strong>JanitorAI</strong> - flexible, but too infrastructure-dependent for me</li>
<li><strong>Talkie / Grok Ani</strong> - interesting, different goals</li>
</ol>
<p>Your ranking can flip completely if you care more about community size or model tuning than relationship continuity.</p>
<hr />
<h2>Who I would recommend LumiChat to</h2>
<p>Try <a href="https://www.lumichat.ink/">LumiChat</a> if you are searching any of these:</p>
<ul>
<li>best Character.AI alternative for long chats</li>
<li>SpicyChat alternative without Lorebook homework</li>
<li>JanitorAI alternative that is fully hosted</li>
<li>PolyBuzz alternative with more story progression</li>
<li>AI companion app with relationship levels and chapters</li>
<li>anime AI chat / virtual partner chat that is not pure feed spam</li>
<li>multilingual AI roleplay platform</li>
</ul>
<p>Start here:</p>
<ul>
<li>Home: <a href="https://www.lumichat.ink/">https://www.lumichat.ink/</a></li>
<li>Discover: <a href="https://www.lumichat.ink/discover">https://www.lumichat.ink/discover</a></li>
<li>Characters: <a href="https://www.lumichat.ink/characters">https://www.lumichat.ink/characters</a></li>
<li>Blog comparisons: <a href="https://www.lumichat.ink/blog">https://www.lumichat.ink/blog</a></li>
</ul>
<hr />
<h2>Final verdict</h2>
<p>The AI companion market is loud.</p>
<p>Most products win one headline feature and lose the next-day experience.</p>
<p>Character.AI wins scale.<br />SpicyChat wins control.<br />JanitorAI wins flexibility.<br />PolyBuzz wins browse velocity.<br />Talkie wins presentation energy.<br />Grok Ani wins "one familiar companion inside a bigger AI."</p>
<p><strong>LumiChat wins the quieter metric:</strong> I kept wanting to continue the same story.</p>
<p>If that is the problem you are actually trying to solve, it deserves a real test - not just another tab you open for three messages and abandon.</p>
<hr />
]]></content:encoded></item></channel></rss>