Agent memory
Help an agent keep the rules it must follow, learn from its tool errors, check the rules before it acts, and learn which actions actually work.
01. Rules and events are different
An agent remembers two kinds of things, and it should treat them differently.
| Kind | Example | How the agent uses it |
|---|---|---|
| Rule | “Refunds over $250 need approval from a human.” | Applies to every future task. Look it up before acting. |
| Event | “Refunded $120 to order 2291 on 3 May.” | Something that happened once. Useful history, never an instruction. |
Store rules on purpose, and let Hebbrix pick up events and lessons from the transcripts you save. Give the agent its own scope: a collection_id plus an agent_id. Memories saved under one agent_id are never returned to another.
import os
import time
import httpx
BASE = os.environ.get("HEBBRIX_BASE_URL", "https://api.hebbrix.com/v1")
hebbrix = httpx.Client(
base_url=BASE,
headers={"Authorization": f"Bearer {os.environ['HEBBRIX_API_KEY']}"},
timeout=60,
)
collection_id = hebbrix.post("/collections", json={"name": "ops-agent"}).json()["id"]
SCOPE = {"collection_id": collection_id, "agent_id": "ops-agent"}02. Store the rules it must follow
Write each standing rule as one plain sentence and store it with POST /v1/memories/raw. Raw memories are kept word for word, so the rule keeps exactly the scope you wrote (“all refunds”, not “this customer's refunds”). Label it with metadata.memory_type: "rule" so type filters find it, and with wait_for_index: true the call waits, for up to a few seconds, until the rule is searchable.
hebbrix.post("/memories/raw", json={
**SCOPE,
"content": "Refunds over $250 need approval from a human before they are issued.",
"metadata": {"memory_type": "rule"},
"wait_for_index": True,
})03. Learn from tool errors
After each task, save the transcript with POST /v1/memories/process, and include each tool error word for word. Every memory Hebbrix extracts gets a memory_type: rule, procedure, preference, fact or event. An agent transcript is stored as the general rules it states plus one event for the task, so lessons are kept apart from one-off history.
job = hebbrix.post("/memories/process", json={
**SCOPE,
"messages": [
{"role": "user", "content": "Send the March invoice to Kestrel GmbH."},
{"role": "assistant", "content": "Calling send_invoice(customer='Kestrel GmbH', month='March')."},
{"role": "user", "content": "Tool error from send_invoice: invoices to customers in the EU must include the customer's VAT number."},
{"role": "assistant", "content": "Added the VAT number and sent the invoice."},
],
}).json()
# Saving runs in the background. Wait only in tests and demos:
while hebbrix.get(f"/memory-jobs/{job['job_id']}").json()["status"] not in ("completed", "failed"):
time.sleep(1)This task becomes a rule (“Invoices to customers in the EU must include the customer's VAT number.”) and one event describing the task. The finished job lists each new memory with its memory_type, and GET /v1/memories returns it too. If a lesson matters for safety, also store it yourself with /memories/raw so its wording is exactly yours.
04. Check the rules before acting
Before a consequential action, search with a description of what the agent is about to do and give the results to the agent's model as context. memory_types limits the search to standing guidance (here rules and procedures), so past events do not crowd it out.
plan = "Issue a $300 refund to Globex for order 2291."
rules = hebbrix.post("/search", json={
**SCOPE,
"query": plan,
"limit": 5,
"memory_types": ["rule", "procedure"],
"prefer_entity_match": True,
}).json()["results"]
for rule in rules:
print("-", rule["content"], f"({rule.get('entity_match')})")Each result says which customers, people or records it names (applies_to) and how that compares with the entities in your query (entity_match: match, partial, mismatch, or unscoped for a general rule that names no one; the response lists the query's entities in query_entities). A rule about another customer shows mismatch, and prefer_entity_match: true ranks those after the ones that apply.
For a yes/no question, POST /v1/search/reason answers from the stored rules and cites them. When a rule sets a threshold, the answer applies it: asked about a $100 refund, it explains that $100 is not above $250, and metadata.rule_application shows the comparison. If it cannot support an answer, answer is null, decision_abstained is true and abstain_reason says why (for example rule_does_not_cover_case or authorization_not_verifiable). Treat that as “ask a human”, never as permission.
check = hebbrix.post("/search/reason", json={
**SCOPE,
"query": "Can I issue a $300 refund without asking a human?",
}).json()
if check["answer"] is None:
print("Ask a human first:", check["abstain_reason"])
else:
print(check["answer"])05. Learn which actions work
When the agent has several allowed ways to handle a situation, Outcome Memory learns from results which one works best. Hebbrix picks an action, your code performs it, and you report whether it worked. The quickstart shows it learning in a short simulation you can run in a minute.
When a person corrects the agent, store the correction with POST /v1/corrections and fetch the relevant ones before acting with GET /v1/corrections/relevant (see the corrections reference). A newer fact saved with /memories/process replaces the older one it contradicts, and the history is kept.
06. Checklist before you ship
- Each agent has its own
agent_id, used on every call. - Standing rules are stored with
/memories/raw, one rule per memory, labelledmemory_type: "rule". - Transcripts, with tool errors word for word, are saved after each task.
- The agent searches rules and procedures before consequential actions, and a
nullanswer means ask a human. - Permission checks live in your code, not in memory.