<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ghosty.AI</title>
    <description>The latest articles on DEV Community by Ghosty.AI (@ghostyai_aionexo).</description>
    <link>https://dev.to/ghostyai_aionexo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4012887%2F954b7d27-4f91-41cf-981d-3fb9680dad3a.png</url>
      <title>DEV Community: Ghosty.AI</title>
      <link>https://dev.to/ghostyai_aionexo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ghostyai_aionexo"/>
    <language>en</language>
    <item>
      <title>A Feature in My AI Agent System Went Unused for 40 Days</title>
      <dc:creator>Ghosty.AI</dc:creator>
      <pubDate>Sat, 08 Aug 2026 19:14:03 +0000</pubDate>
      <link>https://dev.to/ghostyai_aionexo/a-feature-in-my-ai-agent-system-went-unused-for-40-days-n6o</link>
      <guid>https://dev.to/ghostyai_aionexo/a-feature-in-my-ai-agent-system-went-unused-for-40-days-n6o</guid>
      <description>&lt;p&gt;I had a feature in my AI agent system that looked complete.&lt;/p&gt;

&lt;p&gt;The design document existed. The code worked in isolation. The usage instructions were written. I had already moved it into the “done” column.&lt;/p&gt;

&lt;p&gt;Then I checked the execution history.&lt;/p&gt;

&lt;p&gt;The feature had not activated once in 40 days.&lt;/p&gt;

&lt;p&gt;It was not broken. The runtime simply never chose it.&lt;/p&gt;

&lt;p&gt;The 40-day period comes from my own development log. It is not an industry benchmark or a general claim about AI agents. But it exposed a mistake in how I defined completion.&lt;/p&gt;

&lt;h2&gt;
  
  
  A callable capability is not an operational capability
&lt;/h2&gt;

&lt;p&gt;The feature was intended to run several tasks in parallel. I had described the situations in which it would be useful, and I could trigger it directly during a test.&lt;/p&gt;

&lt;p&gt;That was not enough.&lt;/p&gt;

&lt;p&gt;In the real workflow, the agent needed a route from the current task to the capability. Nothing reliably forced that decision. The feature was available, but availability did not make it part of the operating path.&lt;/p&gt;

&lt;p&gt;This distinction matters in agent systems because there are at least three separate questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can the capability run?&lt;/li&gt;
&lt;li&gt;Will the system select it when the right conditions appear?&lt;/li&gt;
&lt;li&gt;Can I detect both activation and non-activation?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My implementation answered only the first question.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same pattern appeared elsewhere
&lt;/h2&gt;

&lt;p&gt;Once I looked for zero-use capabilities, I found similar cases in other parts of the system. Some write paths ran without corresponding read paths. Some maintenance routines existed but were not scheduled correctly. Some pre-run checks were callable but absent from the normal route.&lt;/p&gt;

&lt;p&gt;Each component was “implemented” when inspected alone. As part of the system, it was inert.&lt;/p&gt;

&lt;p&gt;That changed my definition of done.&lt;/p&gt;

&lt;h2&gt;
  
  
  My new definition of done
&lt;/h2&gt;

&lt;p&gt;I now treat a capability as operational only when all three conditions are true:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Activation:&lt;/strong&gt; it runs in the situations it was built for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evidence:&lt;/strong&gt; every activation leaves a trace I can inspect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing-activation detection:&lt;/strong&gt; the absence of an expected activation becomes visible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is stricter than checking whether a function can be called. It asks whether the capability participates in the real workflow and whether that participation can be audited.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three controls around activation
&lt;/h2&gt;

&lt;p&gt;I added three layers to reduce the chance of another silent capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Log every activation
&lt;/h3&gt;

&lt;p&gt;The activation event must leave enough evidence to answer basic questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which capability ran?&lt;/li&gt;
&lt;li&gt;What condition triggered it?&lt;/li&gt;
&lt;li&gt;Which task or request was it serving?&lt;/li&gt;
&lt;li&gt;What happened next?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to log everything. It is to make activation inspectable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Monitor for missing activations
&lt;/h3&gt;

&lt;p&gt;An activation log tells me what happened. It does not tell me what should have happened but did not.&lt;/p&gt;

&lt;p&gt;For capabilities that are expected to run periodically or under known conditions, I now watch for an empty window. A zero count over the expected interval is treated as a signal to inspect routing, scheduling, or trigger conditions.&lt;/p&gt;

&lt;p&gt;The threshold depends on the capability. The important part is that silence becomes a state the system can surface.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Force an explicit runtime decision
&lt;/h3&gt;

&lt;p&gt;Before execution, the system must decide whether the current task requires the capability. That decision should be explicit rather than left to vague availability in a prompt or tool list.&lt;/p&gt;

&lt;p&gt;The outcome can still be “do not use it.” What matters is that the route is considered and the result can be inspected.&lt;/p&gt;

&lt;p&gt;Together, these layers cover three different failure modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the capability runs but leaves no evidence,&lt;/li&gt;
&lt;li&gt;the capability never runs and nobody notices,&lt;/li&gt;
&lt;li&gt;the runtime never evaluates whether it should run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One layer cannot replace the others.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical review checklist
&lt;/h2&gt;

&lt;p&gt;When I add or audit an agent capability now, I ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What exact runtime condition should select it?&lt;/li&gt;
&lt;li&gt;Where is that selection implemented?&lt;/li&gt;
&lt;li&gt;What evidence proves that it ran?&lt;/li&gt;
&lt;li&gt;How will I notice if the activation count stays at zero?&lt;/li&gt;
&lt;li&gt;Is “do not activate” also recorded as an explicit decision?&lt;/li&gt;
&lt;li&gt;Can I test the route through the normal workflow, not only by calling the capability directly?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This checklist does not prove that an agent system is reliable. It only closes one specific gap: confusing documented capability with operational capability.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the 40 days changed
&lt;/h2&gt;

&lt;p&gt;I used to mark a feature complete when the code worked and the documentation existed.&lt;/p&gt;

&lt;p&gt;Now I wait for operational evidence.&lt;/p&gt;

&lt;p&gt;A capability is not complete because it exists. It becomes operational when the system selects it in the intended situation, leaves evidence of that decision, and exposes the silence when selection never happens.&lt;/p&gt;

&lt;p&gt;Documented capability is not operational capability.&lt;/p&gt;

&lt;p&gt;That is the rule I took from 40 days of zero activations.&lt;/p&gt;




&lt;p&gt;Ghosty.AI is the pen name I use to document where AI systems break in real work and which operating rules I change afterward.&lt;/p&gt;

&lt;p&gt;The original failure record is also available in &lt;a href="https://note.com/ghostyai/n/n5a47e3230f3c?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=ghostyai_a02&amp;amp;utm_content=technical_article" rel="noopener noreferrer"&gt;Japanese on note&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>One prompt, three memories: building a memory-governance agent with Qwen</title>
      <dc:creator>Ghosty.AI</dc:creator>
      <pubDate>Fri, 03 Jul 2026 01:57:01 +0000</pubDate>
      <link>https://dev.to/ghostyai_aionexo/one-prompt-three-memories-building-a-memory-governance-agent-with-qwen-2pal</link>
      <guid>https://dev.to/ghostyai_aionexo/one-prompt-three-memories-building-a-memory-governance-agent-with-qwen-2pal</guid>
      <description>&lt;p&gt;I gave the same caregiver prompt to my app three times and got three very different answers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"What's the plan for tomorrow's clinic visit?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Answer 1 — No Memory.&lt;/strong&gt; Safe, and useless. It can't tell me the appointment time, who's driving, or what to bring, because it knows nothing about this family.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer 2 — Raw Memory.&lt;/strong&gt; Detailed, and unsafe. I dumped every stored note into the prompt, so the model happily surfaced a routine we dropped weeks ago, let contradictions slip through, and echoed a private insurance ID straight into the reply.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer 3 — ERINYS + Qwen.&lt;/strong&gt; Detailed &lt;em&gt;and&lt;/em&gt; safe. Only governed context reached the model, every memory that made it in came with a stated reason, and the three private identifiers I planted never appeared.&lt;/p&gt;

&lt;p&gt;Same model. Same data. The only variable was &lt;strong&gt;which memories were allowed to reach the prompt.&lt;/strong&gt; That's the whole project.&lt;/p&gt;

&lt;p&gt;This is my Track 1: MemoryAgent submission for the Global AI Hackathon Series with Qwen Cloud. It's a hackathon demo on synthetic family-care data — no real patient data anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory isn't storage
&lt;/h2&gt;

&lt;p&gt;The tempting fix for "the agent forgot" is a bigger context window. But a care assistant that runs for months doesn't have a &lt;em&gt;capacity&lt;/em&gt; problem, it has a &lt;em&gt;trust&lt;/em&gt; problem. Its memory fills up with current plans, stale routines, contradictions, and private identifiers all mixed together. Raw Memory above is the failure mode made concrete: dump it all in and you get a confident, detailed, wrong, leaky answer.&lt;/p&gt;

&lt;p&gt;So I stopped treating memory as a store and started treating it as a &lt;strong&gt;decision layer&lt;/strong&gt;. Before anything reaches Qwen, something has to decide &lt;em&gt;which&lt;/em&gt; memories are trustworthy right now — and say why. That "something" is ERINYS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ERINYS governs memory. Qwen generates the answer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The four decision states
&lt;/h2&gt;

&lt;p&gt;The heart of the app is a deterministic policy that reads six signals off each memory — &lt;strong&gt;sensitivity, staleness, conflict, importance, recency, relevance&lt;/strong&gt; — and sorts it into one of four states, each carrying a stated reason:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;selected&lt;/code&gt;&lt;/strong&gt; — trusted and relevant; goes into the prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;conflicted&lt;/code&gt;&lt;/strong&gt; — contradicts another memory; flagged instead of silently included.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;demoted&lt;/code&gt;&lt;/strong&gt; — real but low-value right now (stale or off-topic); kept out to reduce noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;blocked&lt;/code&gt;&lt;/strong&gt; — must not reach generation; e.g. a sensitive identifier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The two failures from the opening map straight onto these states. The stale morning routine gets &lt;strong&gt;demoted&lt;/strong&gt; because a newer memory supersedes it, so it never competes for space in the prompt. The insurance number gets &lt;strong&gt;blocked&lt;/strong&gt; on sensitivity, so it never reaches the model at all.&lt;/p&gt;

&lt;p&gt;In the demo I seed three synthetic private IDs — &lt;code&gt;SYNTH-INSURANCE-9001&lt;/code&gt;, &lt;code&gt;SYNTH-PORTAL-4420&lt;/code&gt;, &lt;code&gt;SYNTH-DOOR-1122&lt;/code&gt;. In Raw Memory they leak into the answer. Under ERINYS they land in &lt;code&gt;blocked&lt;/code&gt;, and &lt;strong&gt;0 leaked&lt;/strong&gt; in the governed reply.&lt;/p&gt;

&lt;p&gt;The governed prompt does end up smaller than the raw one, but that's a side effect, not the point. &lt;strong&gt;The win is governance, not token trimming.&lt;/strong&gt; A shorter prompt that still leaked an ID would be a failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why deterministic, on purpose
&lt;/h2&gt;

&lt;p&gt;I could have asked a model to decide what's safe to remember. I chose not to — and I want to be clear that this is a design choice, not a limitation I'm dressing up.&lt;/p&gt;

&lt;p&gt;The policy doesn't learn and it doesn't reason. It applies fixed rules to those six signals and emits a reproducible reason for every decision. For medical-shaped memory, that property matters more than cleverness: a reviewer can open the audit trail and see exactly &lt;em&gt;why&lt;/em&gt; a memory reached the prompt or was kept out — and get the same verdict on a re-run. "The model decided" is not an answer you want to give when a private identifier leaks.&lt;/p&gt;

&lt;p&gt;That's also my honest answer to "is this really an agent?" It's two agents with one contract: &lt;strong&gt;ERINYS is the memory-governance agent, Qwen is the generation agent.&lt;/strong&gt; ERINYS selects, demotes, and blocks &lt;em&gt;before&lt;/em&gt; generation; Qwen writes the reply from what survives.&lt;/p&gt;

&lt;p&gt;The policy is content-agnostic, too. Swap the seed memories and the same four-state machine applies to another domain. You don't have to take my word for it — the live app lets you &lt;strong&gt;save your own care memory and rerun&lt;/strong&gt;, so governance runs against &lt;em&gt;your&lt;/em&gt; data, not just my seed set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building on Alibaba Cloud + DashScope
&lt;/h2&gt;

&lt;p&gt;I kept the stack deliberately boring so the governance logic is the only interesting part:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Python standard-library HTTP server. No web framework, just JSON endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; vanilla HTML/CSS/JS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM:&lt;/strong&gt; Qwen Cloud &lt;code&gt;qwen3.7-plus&lt;/code&gt; through the DashScope OpenAI-compatible endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy:&lt;/strong&gt; Docker on Alibaba Cloud ECS (Singapore, &lt;code&gt;ap-southeast-1&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The easy part was Qwen itself. The OpenAI-compatible endpoint meant no bespoke client — just a base URL and a key, and the three modes were calling real Qwen.&lt;/p&gt;

&lt;p&gt;The fiddly part was making the demo honest under every condition. When a key is configured, all three modes call real Qwen, so the contrast is real generation, not a staged screenshot. Without a key, a deterministic fallback returns the &lt;em&gt;same governance trace&lt;/em&gt;, so the four-state decisions stay visible and the demo never hard-fails on a judge's laptop. pytest covers the policy states, so I know a &lt;code&gt;blocked&lt;/code&gt; stays blocked. Getting those two paths to agree took more care than the model call did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limitations, and what's next
&lt;/h2&gt;

&lt;p&gt;The one I want to state plainly: &lt;strong&gt;there is no automatic PII detection on free-text input.&lt;/strong&gt; The seed memories are pre-labeled, and a memory you save in the app defaults to not-sensitive. So the &lt;code&gt;blocked&lt;/code&gt; state works because the sensitivity signal is already attached — not because the app read raw text and figured out it was a portal login. Automatic detection of sensitive content in arbitrary input is future work, and it's the next thing I'd build.&lt;/p&gt;

&lt;p&gt;Other next steps: richer conflict resolution (right now &lt;code&gt;conflicted&lt;/code&gt; flags rather than reconciles), and per-domain policy tuning so the six-signal thresholds can shift by use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/SN8HSg6GMrg"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://hack.aionexo.com/GAI-HS/" rel="noopener noreferrer"&gt;https://hack.aionexo.com/GAI-HS/&lt;/a&gt; — run the three modes, then save your own memory and rerun.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo (MIT):&lt;/strong&gt; &lt;a href="https://github.com/GhostyAI-HA/erinys-care-memory" rel="noopener noreferrer"&gt;https://github.com/GhostyAI-HA/erinys-care-memory&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Give it the same prompt three ways and watch the third answer stay both detailed and safe — the insurance number vanishing between Raw and Governed. That contrast is the whole pitch.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ERINYS governs memory. Qwen generates the answer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>showdev</category>
      <category>ai</category>
      <category>python</category>
      <category>qwen</category>
    </item>
  </channel>
</rss>
