{"slug": "show-hn-trace-open-source-self-organizing-memory-for-llm-agents-pypi", "title": "Show HN: Trace – open-source, self-organizing memory for LLM agents (PyPI)", "summary": "TRACE, an open-source Python library for self-organizing long-term memory in LLM agents, was released on PyPI. It organizes conversation history into a hierarchical B+Tree to enable efficient, multi-path retrieval without full-history prompt injection. The library aims to reduce token costs and hallucination by surgically retrieving relevant context, addressing failures of sliding windows and standard RAG for persistent memory.", "body_md": "A hierarchical, background memory tree for long-running LLM agents.\n\nTRACE organizes conversation history into a structured semantic map, allowing for efficient, multi-path retrieval of long-term context without relying on full-history prompt injection.\n\nThis repo has two parts — they are completely independent:\n\n- 🧠\n— the lightweight memory engine. Install it, import it, and integrate it into your own app. Zero UI, zero bloat.`trace_memory/`\n\n- 🖥️\n— an optional demo chatbot built on top of the engine. Use it to test TRACE live, explore how it works, and run experiments. You do not need it to use TRACE.`nexus_terminal/`\n\nTRACE is a Python library that gives your LLM agent **structured, searchable, self-organizing long-term memory**.\n\nInstead of naïvely stuffing an ever-growing chat log into every prompt, TRACE organises every conversation exchange into a **hierarchical B+Tree of named topic branches**. When the agent needs context, TRACE performs a fast cosine similarity search across topic summaries and retrieves only the *surgically relevant* branches — not the entire history.\n\nAt rest (while the agent is not actively chatting), TRACE's **background reorganizer** evaluates the entire tree against four strict axioms and merges semantically related branches under shared parents — inspired by memory consolidation processes.\n\nThe result: an agent **designed to preserve cross-session constraints through hierarchical retrieval**, that reduces hallucination of stale context, and operates at **a fraction of the token cost** of sliding-window or full-history approaches.\n\nStandard RAG works extremely well for:\n\n- documentation search\n- knowledge bases\n- code retrieval\n- enterprise search\n\nThe problem isn't RAG. The problem is using RAG as persistent memory.\n\n| Failure Mode | What Happens | Real Impact |\n|---|---|---|\nTemporal Blindness |\nRAG retrieves semantically similar chunks regardless of when they occurred. Old, overridden decisions surface alongside current ones. |\nAgent contradicts itself, repeats resolved problems, or reinstates abandoned plans. |\nContext Rot |\nSliding windows drop early messages as the conversation grows. | Constraints set in message 3 are gone by message 50. The agent forgets that Sarah is allergic to peanuts. |\nLossy Summarization |\nCompressing history into a single paragraph erases detail and nuance. | Agent loses track of branching plans, multi-hop constraints, and edge-case handling agreed upon earlier. |\n\nA fixed-size sliding window is the most common approach — and it works well for on-demand, single-session queries where you need detailed, verbatim access to recent exchanges. But it is fundamentally unsuited for long-term agent memory:\n\n**No semantic awareness**: message #1 and message #200 are weighted equally as long as they fit.** Guaranteed forgetting**: anything outside the window is permanently gone from the agent's context.** No structure**: a flat list tells the LLM nothing about*which*topics are related or*which*branch an earlier constraint belongs to.\n\nFor a simple chatbot or a document Q&A tool, a sliding window is perfectly fine. For a long-running agent performing multi-step tasks across sessions, it is catastrophic.\n\nMemGPT is incredibly powerful, but it functions like a full operating system with tiered memory (RAM, disk) that the LLM must explicitly learn to manage via function calls. This introduces significant overhead and requires highly capable models.\n\nTRACE is meant to be a **lightweight, drop-in component**, not a full runtime environment. It focuses specifically on modelling conversations as a hierarchical tree to natively surface multi-hop constraints without forcing the LLM to actively manage its own memory banks.\n\nTRACE builds on the open-source **ChatIndex** architecture (credit: Mingtian Zhang, Ray, VectifyAI). A modified version of ChatIndex's core logic is bundled directly within TRACE, which models conversation history as a B+Tree:\n\n**Leaf nodes (MessageNodes)**— raw user/assistant exchanges.** Internal nodes (TopicNodes)**— LLM-generated topic labels and summaries for each branch.** Root**— a virtual anchor node.\n\nEvery time an exchange is added (`tree.add()`\n\n), TRACE forces the creation of a **new TopicNode** containing exactly one exchange. The LLM determines if it continues the current topic or starts a new branch. If it continues the topic, the new node is simply chained as a direct child of the previous one. This deep chronological chaining solves context truncation and ensures perfect, granular summarization for every single exchange.\n\nThis gives TRACE a **deep, structured map of the entire conversation history** — not a flat log — with highly granular topic metadata at every single link in the chain.\n\n**What TRACE adds to ChatIndex**: ChatIndex primarily retrieves context through a single traversal path. While effective for hierarchical exploration, information spread across multiple semantically related branches may require multiple retrieval steps. TRACE augments this with vector-based retrieval across topic summaries, allowing context from multiple branches to be surfaced simultaneously.\n\n**The problem**: A single ancestry path only captures the current conversational thread. Cross-branch constraints (e.g., \"Sarah's allergy\" in Branch 1, \"party cake\" in Branch 3) are invisible unless both branches are active.\n\n**The solution**: Every time the agent needs to respond, `PromptSynthesizer`\n\nruns a **cosine similarity search** against the VectorDatabase of embedded topic summaries:\n\n```\nUser query  →  embed()  →  query vector\n                              ↓\n               VDB: cosine search across ALL topic summaries\n                              ↓\n               Filter: keep all nodes above base cosine threshold\n                              ↓\n               Walk full ancestry of each qualifying node\n                              ↓\n               Deduplicate shared ancestor nodes\n                              ↓\n               Rank by similarity → take Top-3 paths\n                              ↓\n               Format compact multi-path context block\n```\n\nThis means the LLM receives context from **multiple relevant branches simultaneously**, not just the current thread — saving thousands of tokens compared to full-history injection, while synthesising information across branches that were never explicitly connected.\n\n**The cross-branch synthesis stress test** (validated with `gpt-oss-20b`\n\nvia NVIDIA NIM):\n\n```\nBranch 1: \"Sarah is allergic to peanuts.\"\nBranch 2: \"Weather in Tokyo?\" (noise)\nBranch 3: \"Planning Sarah's surprise party, baking a cake.\"\nBranch 4: \"Fixing a bike tire?\" (noise)\nBranch 5: \"Found a Thai Peanut Butter Cake recipe.\"\n\nPrompt: \"I'm making the Peanut Butter Cake for Sarah's party. Good idea?\"\n\nResult: The AI aggressively stopped the user.\n        The VDB surgically bypassed Branches 2 & 4 (noise),\n        retrieved Branches 1, 3, and 5,\n        and synthesised them to catch the life-threatening allergy conflict\n        — without any explicit link between them ever being made.\n```\n\n**The problem**: Long conversations naturally fragment. Topics discussed in different sessions may be semantically identical but live in separate branches, causing redundant retrieval and diluted context.\n\n**The solution**: `tree.reorganize()`\n\nruns a conservative, four-rule-guarded merge pass:\n\n```\nPhase 1 — Collect all frozen (inactive) TopicNodes\nPhase 2 — Generate missing summaries; embed all candidates\nPhase 3 — Compute pairwise cosine similarity; for each pair above threshold, apply 4 axioms:\n              Axiom 1 — Chronological Guard\n              Axiom 2 — Frozen State check\n              Axiom 3 — Similarity threshold (default 0.55)\n              Axiom 4 — LLM Veto\n           If all 4 pass → merge (newer becomes child of older)\nPhase 4 — Optional: prune trivial leaf messages\n```\n\n| Axiom | Rule | Why |\n|---|---|---|\n1. Chronological Guard |\nThe older node absorbs the newer one — never the reverse. | Preserves temporal ordering. The past cannot be restructured to appear after the present. |\n2. Frozen State |\nOnly nodes outside the currently active ancestry path may be merged. | The live conversation thread is never touched. Zero risk of corrupting the active agent state. |\n3. Similarity Threshold |\nCosine similarity between embeddings must exceed the threshold (default: `0.55` ). |\nPre-filters pairs using pure math before wasting an LLM call. |\n4. LLM Veto |\nThe LLM independently confirms the merge makes semantic sense. | Catches false positives (e.g., two topics both mentioning \"Python\" — one about snakes, one about code). If vetoed, the merge is aborted. |\n\n**Analogy**: Just like a human brain during sleep — when the body is at rest, the brain doesn't switch off. It replays the day's events, consolidates important memories into long-term storage, and prunes connections that are no longer relevant. TRACE does exactly this: when the agent is idle, it reorganizes its memory graph, surfaces hidden connections across branches, and prunes redundancy — all without corrupting the live agent state.\n\nShort, throwaway exchanges (\"ok\", \"thanks\", \"got it\") pollute the tree with noise that wastes tokens and dilutes retrieval quality.\n\nWhen `prune_trivial_leaves=True`\n\nis passed to `reorganize()`\n\n, TRACE detects MessageNodes where both the user and assistant messages are under 20 words, and **soft-archives** them — moving them to `tree._archived_nodes`\n\ninstead of hard-deleting them. They are persisted to disk in case you ever need them, but they are excluded from all future retrieval and prompt synthesis.\n\n```\npip install trace-memory==1.0.7\n```\n\nIf you already have a chat loop and just want to plug TRACE in, this is all you need. Since TRACE includes a powerful built-in local embedder (`BAAI/bge-base-en-v1.5`\n\n), you don't even need to configure your own embedding model!\n\n``` python\nfrom trace_memory.ctree import CTree\nfrom trace_memory.vector_db import VectorDatabase\nfrom trace_memory.prompt_synthesizer import PromptSynthesizer\n\n# 1. Boot the tree and attach the Vector DB\ntree = CTree(api_key=\"sk-...\", model=\"gpt-4o-mini\")\ntree.vdb = VectorDatabase(\"session.db\")\n\n# 2. Setup the Prompt Synthesizer\nsynth = PromptSynthesizer(ctree=tree, vector_db=tree.vdb)\n\n# 3. Each turn: build prompt → call LLM → store exchange\nwhile True:\n    user_input = input(\"You: \")\n    system_prompt = synth.synthesize_prompt(\n        user_query      = user_input,\n        query_vector    = embed(user_input),\n        active_node     = tree.current_node,\n        recent_messages = tree.conversation[-6:],\n    )\n    response = client.chat.completions.create(\n        model    = \"gpt-4o-mini\",\n        messages = [{\"role\": \"system\", \"content\": system_prompt},\n                    *tree.conversation[-10:],\n                    {\"role\": \"user\", \"content\": user_input}],\n    )\n    reply = response.choices[0].message.content\n    tree.add([{\"role\": \"user\", \"content\": user_input},\n              {\"role\": \"assistant\", \"content\": reply}])\n    print(f\"AI: {reply}\")\n\n# 3. When the agent is idle, consolidate memory\nstats = tree.reorganize(similarity_threshold=0.55, prune_trivial_leaves=True)\n```\n\nTRACE comes with a fully-featured, gorgeous Terminal UI chatbot out of the box. It is designed as a lightweight sandbox just for testing out TRACE—seeing how it works, running tests, and exploring the engine without heavy frontend overhead.\n\n**Key Features included in the terminal:**\n\n**Dynamic VRAM Swapping**: Hot-swaps models on the fly (unloading Text, loading Vision) to prevent local GPU crashes.** Live Web Search**: Pre-generation routing secretly checks DuckDuckGo to prevent hallucinations on current events.** Visualizing the B+Tree**: Use`/tree`\n\nto instantly print and inspect the live hierarchical memory map.**Multimodal Ingestion**: Drop images in the folder and use`/ingest`\n\nto extract rich descriptions directly into long-term memory.**Gorgeous TUI**: Threaded background spinners and ANSI colors so the UI never freezes while testing.\n\nTo run it immediately:\n\n**Navigate to the terminal folder:**\n\n```\ncd nexus_terminal\n```\n\n**Install the UI dependencies:**\n\n```\npip install -r requirements.txt\n```\n\n**Configure your models:** Rename`.env.example`\n\nto`.env`\n\nand adjust the models/URLs to point to your local LM Studio or OpenAI endpoints.**Boot the engine:**\n\n```\npython terminal.py\n```\n\nThe hierarchical conversation memory tree.\n\n``` python\nfrom trace_memory import CTree\nCTree(\n    max_children:   int = 5,\n    api_key:        str = None,        # optional: falls back to OPENAI_API_KEY env var or \"lm-studio\"\n    base_url:       str = None,        # optional: route to custom endpoints (e.g. vLLM, Ollama, LM Studio)\n    model:          str = \"gpt-4o-mini\",\n    auto_save_path: str = None,        # auto-saves tree structure (not VDB) after every add() if set\n    embed_fn:       Callable = None,   # optional: inject your embed function at construction time\n)\ntree.vdb       = VectorDatabase(\"session.db\")   # VDB for semantic retrieval\ntree.embed_fn = embed                           # callable(text: str) -> List[float]\n```\n\nIngest one completed exchange into the tree.\n\n```\ntree.add([\n    {\"role\": \"user\",      \"content\": \"What is quantum entanglement?\"},\n    {\"role\": \"assistant\", \"content\": \"Quantum entanglement is ...\"},\n])\n\n# With an optional system/context message\ntree.add([\n    {\"role\": \"system\",    \"content\": \"[Tool result]: 42.3°C\"},\n    {\"role\": \"user\",      \"content\": \"Is that dangerous?\"},\n    {\"role\": \"assistant\", \"content\": \"Yes, 42.3°C is critically high ...\"},\n])\n```\n\nRun one self-healing reorganization pass.\n\n```\nstats = tree.reorganize(\n    embed_fn              = embed,   # optional: overrides tree.embed_fn\n    similarity_threshold  = 0.60,   # raise for more conservative merges\n    prune_trivial_leaves  = True,    # archive short throwaway messages\n)\nprint(stats)\n# {'merged': 3, 'pruned': 7, 'skipped': 12, 'duration_secs': 4.2}\n```\n\n**When to call**: Periodically when the agent is idle. Not after every message.\n\nPersist the tree to JSON.\n\n```\ntree.save(\"sessions/chat_001.json\", save_conversation=True)\n```\n\n`save_conversation=True`\n\nembeds the raw message list so the session can be fully restored later.\n\n`CTree.load(filepath: str, api_key: str = None, base_url: str = None, model: str = None, embed_fn: Callable = None) -> CTree`\n\nRestore a tree from a JSON file.\n\n```\ntree = CTree.load(\"sessions/chat_001.json\", api_key=\"sk-...\", embed_fn=embed)\ntree.vdb = VectorDatabase(\"sessions/chat_001.db\")\n```\n\nReturn the ordered ancestry chain from root down to `node`\n\n.\n\n```\npath = tree.get_ancestors(tree.current_node, include_self=True, exclude_root=True)\nfor node in path:\n    print(f\"  {node.topic_name}: {node.summary}\")\n```\n\nManually trigger LLM summarisation of all frozen branches.\n\nCalled automatically during `save()`\n\nand internally during `reorganize()`\n\n. Can be used to manually pre-warm summaries if desired.\n\nPretty-print the tree to stdout.\n\n```\ntree.print_tree(show_messages=True)\n```\n\nExample output:\n\n```\nROOT (sub-nodes: 3)\n  ├─ Physics Discussions [0:12] (6 msgs)\n     Covered quantum entanglement and black hole thermodynamics.\n    ├─ Quantum Entanglement [0:6] (3 msgs)\n    ├─ Black Holes [6:12] (3 msgs)\n  ├─ Party Planning [12:20] (4 msgs)\n     Planning Sarah's surprise birthday party logistics.\n```\n\n| Attribute | Type | Description |\n|---|---|---|\n`tree.conversation` |\n`List[dict]` |\nFlat list of all raw messages in chronological order. |\n`tree.current_node` |\n`TopicNode` |\nThe currently active topic branch. |\n`tree.root` |\n`TopicNode` |\nThe virtual root of the tree. |\n`tree._archived_nodes` |\n`List[MessageNode]` |\nSoft-archived trivial leaf messages. |\n`tree.auto_save_path` |\n`str | None` |\nIf set, auto-saves the tree structure (not the VDB) after every `add()` . |\n\nA local SQLite vector store with two active tables: conversation vectors and topic summaries.\n\nNote on Scaling:TRACE uses SQLite (`sqlite3`\n\n) for storage,`numpy`\n\nfor fast cosine similarity, and`struct`\n\nfor compact binary packing — all chosen to keep the footprint small while running on any machine. For massive scale (millions of vectors), swap this module for FAISS or Chroma.\n\n``` python\nfrom trace_memory import VectorDatabase\nvdb = VectorDatabase(\"path/to/session.db\")  # creates the DB if it doesn't exist\n```\n\nStore an embedded past conversation message for cross-thread recall.\n\nNote\n\nAs of TRACE 1.0.4, `CTree.add()`\n\ncalls this **automatically** behind the scenes. You only need to call this manually if you are managing the Vector DB independently of a CTree.\n\n``` python\nfrom trace_memory import ConversationVector\nimport time, uuid\n\nmsg = ConversationVector(\n    message_id    = str(uuid.uuid4()),\n    message_index = 0,\n    role          = \"user\",\n    text          = \"Sarah is allergic to peanuts.\",\n    embedding     = embed(\"Sarah is allergic to peanuts.\"),\n    timestamp     = time.time(),\n    thread_path   = \"ROOT → Health Constraints → Allergies\",\n)\nvdb.add_conversation_message(msg)\n```\n\nRetrieve semantically similar past messages from any branch.\n\n```\nrecalls = vdb.search_conversation(\n    query_vector   = embed(\"Is the cake safe for Sarah?\"),\n    top_k          = 3,\n    min_similarity = 0.45,\n)\nfor r in recalls:\n    print(f\"[{r.similarity:.2f}] {r.thread_path} — {r.role}: {r.text}\")\n```\n\nInsert or update a topic node's embedding. Called automatically by `CTree`\n\nwhen a node is frozen and summarised.\n\nUsed internally by `PromptSynthesizer`\n\nfor surgical multi-path retrieval.\n\n```\nhits = vdb.search_topic_summaries(\n    query_vector   = embed(\"peanut allergy constraint\"),\n    top_k          = 3,\n    min_similarity = 0.35,\n)\n# Returns: [{\"node_id\": \"...\", \"topic_name\": \"...\", \"summary\": \"...\", \"similarity\": 0.82}, ...]\n```\n\nRemove a topic embedding by node ID. Called automatically during `reorganize()`\n\nwhen a node is moved.\n\nAssembles the full RAG-enriched system prompt.\n\n``` python\nfrom trace_memory import PromptSynthesizer\n\nsynth = PromptSynthesizer(ctree=tree, vector_db=vdb)\nsystem_prompt = synth.synthesize_prompt(\n    user_query             = \"Is the cake safe for Sarah?\",\n    query_vector           = embed(\"Is the cake safe for Sarah?\"),\n    active_node            = tree.current_node,\n    recent_messages        = tree.conversation[-6:],\n    top_k_docs             = 3,     # max topic branch paths to surface\n    top_k_history          = 2,     # max past messages to recall\n    min_history_similarity = 0.50,  # min cosine score for conversation recall\n)\n```\n\nThe returned string is a complete system prompt. Pass it directly as the `system`\n\nrole message to your LLM.\n\n```\nConversationVector(\n    message_id:    str,\n    message_index: int,\n    role:          str,   # \"user\" | \"assistant\" | \"system\"\n    text:          str,\n    embedding:     List[float],\n    timestamp:     float,       # unix timestamp\n    thread_path:   str,         # e.g. \"ROOT → Physics → Black Holes\"\n    similarity:    float = 0.0,\n)\npython\nfrom trace_memory import CTree\n# Connect directly via parameters\ntree = CTree(\n    base_url=\"http://127.0.0.1:1234/v1\",\n    api_key=\"lm-studio\",\n    model=\"meta-llama-3.1-8b-instruct\"\n)\n\n# Or set environment variables globally\nimport os\nos.environ[\"OPENAI_BASE_URL\"] = \"http://127.0.0.1:1234/v1\"\nos.environ[\"OPENAI_API_KEY\"]  = \"lm-studio\"\n```\n\nOr use a `.env`\n\nfile in your project root:\n\n```\nOPENAI_BASE_URL=http://127.0.0.1:1234/v1\nOPENAI_API_KEY=lm-studio\npython\nfrom trace_memory import CTree\ntree = CTree(api_key=\"sk-...\", model=\"gpt-4o-mini\")\n```\n\nPoint the `base_url`\n\nto your endpoint.\n\n```\ntree = CTree(\n    api_key=\"your-nvidia-api-key\",\n    base_url=\"https://integrate.api.nvidia.com/v1\",\n    model=\"meta/llama-3.1-8b-instruct\"\n)\n```\n\n**Do not use \"Reasoning\" models (DeepSeek R1, Claude 3.7 Sonnet thinking mode, o1) for the internal CTree engine.**\n\nWhen `CTree`\n\nruns in the background, it performs simple, deterministic classification tasks (e.g., naming a topic in 3 words, or extracting a JSON boolean). Reasoning models are built for complex logic and will waste massive amounts of compute \"thinking\" about simple tasks. Worse, reasoning models often exceed TRACE's strict internal token limits (`max_tokens=50`\n\nor `200`\n\n) designed to keep background operations fast, causing the LLM to get cut off mid-thought and breaking the internal JSON parsers.\n\n**For the** Always use a fast, standard model (e.g.,`CTree`\n\ninternal model:`gpt-4o-mini`\n\n,`meta-llama-3.1-8b-instruct`\n\n).**For your actual chat application:** You can (and should!) use whatever massive reasoning model you prefer to generate the final response to the user. TRACE is completely decoupled from your front-end chat completion.\n\nTRACE has been tested against standard Agent Memory Benchmarks. A comparison with other memory architectures demonstrates its efficiency in long-context retrieval tasks.\n\nTRACE was validated against five adversarial test scenarios:\n\n| Test | Description | Result |\n|---|---|---|\nNeedle in a Haystack |\nA critical constraint buried deep in a 200-message session | ✅ Retrieved with >0.25 cosine score |\nMemory Overwrites |\nUser updates a constraint (\"actually, Sarah can eat nuts now\") | ✅ Newer node supersedes older via Chronological Guard |\nSemantic Drift (Veto Test) |\nTwo topics share keywords but are in different domains (Python the snake vs. Python the language) | ✅ LLM Veto correctly aborted the merge |\nShip of Theseus |\nGradual topic drift — same entity discussed across 10 different branches | ✅ Reorganizer correctly consolidated into a shared parent |\nMulti-Hop Reasoning |\nAnswer requires synthesising info from 3 non-adjacent branches (allergy + party + recipe) | ✅ Surgical retrieval surfaced all 3; LLM synthesised the conflict |\n\n| Variable | Default | Description |\n|---|---|---|\n`OPENAI_BASE_URL` |\n`https://api.openai.com/v1` |\nLLM API endpoint (standard PyPI usage) |\n`OPENAI_API_KEY` |\n`None` |\nYour LLM API key |\n\nSet these in a `.env`\n\nfile or directly in your shell.\n\nTRACE loads `.env`\n\nautomatically if `python-dotenv`\n\nis installed.\n\n| Package | Version | Required |\n|---|---|---|\n`openai` |\n≥ 1.0.0 | ✅ Yes |\n`python-dotenv` |\n≥ 1.0.0 | ✅ Yes |\n`sentence-transformers` |\n≥ 2.2.0 | ✅ Yes (local embed fallback) |\n`numpy` |\n≥ 1.21.0 | ✅ Yes (cosine similarity) |\n`sqlite3` |\nbuilt-in | ✅ Yes (no install needed) |\n`struct` |\nbuilt-in | ✅ Yes (no install needed) |\n\nPull requests are welcome. For major changes, please open an issue first.\n\nAreas where contributions are especially valuable:\n\n- Additional embedding model adapters (Sentence Transformers, Cohere, etc.)\n- Async support for\n`add()`\n\nand`reorganize()`\n\n- Web UI for tree visualisation\n\nThis project is licensed under the Apache License 2.0. See [LICENSE](/husain34/TRACE/blob/main/LICENSE) for details.\n\n*Note: This project includes code from ChatIndex, licensed under Apache 2.0. See NOTICE for details.*\n\n*Built by Husain Ghulam.*", "url": "https://wpnews.pro/news/show-hn-trace-open-source-self-organizing-memory-for-llm-agents-pypi", "canonical_source": "https://github.com/husain34/TRACE", "published_at": "2026-07-08 09:49:12+00:00", "updated_at": "2026-07-08 10:00:09.861126+00:00", "lang": "en", "topics": ["large-language-models", "ai-agents", "ai-tools", "ai-infrastructure", "developer-tools"], "entities": ["TRACE", "PyPI", "ChatIndex", "Mingtian Zhang", "VectifyAI", "MemGPT"], "alternates": {"html": "https://wpnews.pro/news/show-hn-trace-open-source-self-organizing-memory-for-llm-agents-pypi", "markdown": "https://wpnews.pro/news/show-hn-trace-open-source-self-organizing-memory-for-llm-agents-pypi.md", "text": "https://wpnews.pro/news/show-hn-trace-open-source-self-organizing-memory-for-llm-agents-pypi.txt", "jsonld": "https://wpnews.pro/news/show-hn-trace-open-source-self-organizing-memory-for-llm-agents-pypi.jsonld"}}