Medical Agent with MeTTa
Overview
This guide shows how to integrate SingularityNET's MeTTa (Meta Type Talk) knowledge graph with Fetch.ai's uAgents framework. The sample is a toy demo: it looks up illustrative symptom → disease → treatment → side-effect facts in MeTTa, then uses ASI:One to classify intent and humanize the reply. It is not medical advice and is not a substitute for professional care.
Tested combo: Python 3.10–3.12, uagents>=0.22.5 (needs uagents-core 0.3.x), hyperon>=0.2.6. Chat Protocol samples need this runtime; Python 3.8 is not supported.
What is MeTTa?
MeTTa (Meta Type Talk) is SingularityNET's multi-paradigm language for declarative and functional computations over knowledge (meta)graphs. Official docs: MeTTa language and Hyperon. It provides:
- Structured Knowledge Representation: Organize information in logical, queryable formats
- Symbolic Reasoning: Perform complex logical operations and pattern matching
- Knowledge Graph Operations: Build, query, and manipulate knowledge graphs
- Space-based Architecture: Knowledge stored as atoms in logical spaces
Installation & Setup
Prerequisites
Before you begin, ensure you have:
- Python 3.10+ (3.10–3.12 recommended for
uagents0.22.x). On Windows usepy -3.10; on WSL/macOS/Linux usepython3. - pip package manager
- An ASI:One API key from the ASI:One API keys dashboard (not only the asi1.ai homepage)
Create a project folder and a virtual environment (do not install into system Python):
# macOS / Linux / WSL
python3 -m venv .venv
source .venv/bin/activate
# Windows PowerShell
py -3.10 -m venv .venv
.\.venv\Scripts\Activate.ps1
Create a .env file (never commit real secrets):
ASI_ONE_API_KEY=your_key_here
AGENT_SEED=change-me-to-a-unique-local-seed
# Optional: set LEARN=1 only if you want the demo to persist LLM guesses into the graph
# LEARN=1
Installation Options
Option 1: Install All Dependencies at Once (Recommended)
Create a requirements.txt file with one package per line:
openai>=1.0.0
hyperon>=0.2.6
uagents>=0.22.5
uagents-core>=0.3.5,<0.4.0
python-dotenv>=1.0.0
uagents 0.22.x is tested with uagents-core 0.3.x (Chat Protocol). Newer uagents 0.23.x needs uagents-core>=0.4.0; keep the pins above unless you upgrade the whole stack.
Install all dependencies with one command:
python3 -m pip install -r requirements.txt
On Windows: py -3.10 -m pip install -r requirements.txt.
Option 2: Verify Hyperon First
Use this only to confirm Hyperon/MeTTa installs on your machine. You still need Option 1 (requirements.txt) for uagents, openai, and python-dotenv.
python3 -m pip install hyperon
python3 -c "from hyperon import MeTTa; print('Hyperon installed successfully!')"
Windows Installation Guide
Hyperon on native Windows is often painful. WSL (Ubuntu) is recommended. If you stay on native Windows and hit build errors, see this video: Hyperon Installation on Windows.
Written WSL path:
- Install WSL and Ubuntu.
- Inside WSL: install Python 3.10+, create the venv above, then
pip install -r requirements.txt. - Run
python3 agent.pyfrom the project folder shown below.
Project layout
Imports in agent.py use the metta package. Create this tree (a flat folder of four .py files will raise ImportError):
project/
agent.py
metta/
__init__.py
knowledge.py
medicalrag.py
utils.py
.env
requirements.txt
Create empty metta/__init__.py. Run from project/:
python3 agent.py
Windows: py -3.10 agent.py.
This page is the canonical sample. Copy the files below into that tree.
Architecture Overview

The code pipeline (ASI:One chat does not classify intent for you):
flowchart LR
user[User or ASI:One chat]
handler[Chat Protocol handler]
llm[Agent LLM: intent plus keyword]
lookup[MeTTa knowledge lookup]
humanize[Humanize plus disclaimer]
user --> handler --> llm --> lookup --> humanize --> user
Alt text: User or ASI:One sends chat text to the Chat Protocol handler. The agent LLM classifies intent and a keyword, MeTTa looks up the toy graph, then the agent humanizes the answer and sends a disclaimer-prefixed reply.
Architecture pipeline: User / ASI:One Chat → Chat Protocol handler → agent LLM classifies intent + keyword → MeTTa knowledge lookup (not vector RAG) → humanized reply with disclaimer → User.
Core Integration Concepts
1. MeTTa Knowledge Graph Structure
MeTTa organizes knowledge as atoms in logical spaces. Use one convention: diseases, symptoms, and treatment keys as S(...); free-text FAQ answers as ValueAtom. Multi-word names use underscores (stomach_upset), never raw spaces or parentheses inside query interpolation.
from hyperon import MeTTa, E, S, ValueAtom
metta = MeTTa()
metta.space().add_atom(E(S("symptom"), S("fever"), S("flu")))
metta.space().add_atom(E(S("treatment"), S("flu"), S("antiviral_drugs")))
metta.space().add_atom(E(S("side_effect"), S("antiviral_drugs"), ValueAtom("nausea, dizziness")))
metta.space().add_atom(E(S("faq"), S("hi"), ValueAtom("Hello! How can I assist you today?")))
Key MeTTa Elements:
- E (Expression): Creates logical expressions
- S (Symbol): Represents symbolic atoms
- ValueAtom: Stores string values (FAQ text, side-effect descriptions)
- Space: Container where atoms are stored and queried
2. Pattern Matching and Querying
# Query syntax: !(match &self (relation subject $variable) $variable)
query_str = '!(match &self (symptom fever $disease) $disease)'
results = metta.run(query_str)
# Results include flu for the toy graph
Query Components:
&self: References the current space$variable: Pattern matching variables that capture results!(match ...): Query syntax for pattern matching
Never interpolate unsanitized user/LLM text into MeTTa. Only simple [a-z0-9_]+ symbols are allowed.
3. uAgent Chat Protocol Integration
The following is an excerpt. Full Protocol construction is in agent.py. process_query returns a dict; send the humanized_answer string (plus disclaimer), not the dict.
from uagents_core.contrib.protocols.chat import (
ChatMessage,
ChatAcknowledgement,
TextContent,
chat_protocol_spec,
)
@chat_proto.on_message(ChatMessage)
async def handle_message(ctx: Context, sender: str, msg: ChatMessage):
response = process_query(user_query, rag, llm)
answer = response.get("humanized_answer", "I could not process that query.")
await ctx.send(sender, create_text_chat(answer))
mailbox=True on Agent(...) is the Agentverse mailbox flag (an inbox so a local agent stays reachable). Do not import mailbox — that is Python's stdlib email-mailbox module and is unused here.
publish_agent_details=True publishes the agent's profile/details to Agentverse when the mailbox connects. Use it for discoverable demos; turn it off if you do not want the profile updated automatically.
4. Knowledge lookup (not vector RAG)
MedicalRAG in this sample is a MeTTa retriever: pattern-match on the toy graph, then an LLM humanizes the result. It does not use embeddings or document RAG. Dynamic graph writes from LLM guesses are off unless LEARN=1 is set (unsafe for anything beyond a local experiment).
Core Components
agent.py: Main uAgent with Chat Protocolmetta/knowledge.py: Toy MeTTa graph (illustrative only — not clinical knowledge)metta/medicalrag.py: MeTTa lookup helpersmetta/utils.py: Intent classification and query processing
Implementation Guide
Step 1: Define Your Knowledge Domain
Create metta/knowledge.py. Edges are only symptom → disease, disease → treatment, treatment → side_effect, plus FAQ keys. Treatments are separate atoms so side-effect lookup works (for example antiviral_drugs, not one mashed string).
from hyperon import MeTTa, E, S, ValueAtom
def initialize_knowledge_graph(metta: MeTTa):
"""Toy graph for the tutorial. Not clinical knowledge."""
# Symptoms → diseases
metta.space().add_atom(E(S("symptom"), S("fever"), S("flu")))
metta.space().add_atom(E(S("symptom"), S("cough"), S("flu")))
metta.space().add_atom(E(S("symptom"), S("nausea"), S("flu")))
metta.space().add_atom(E(S("symptom"), S("headache"), S("migraine")))
metta.space().add_atom(E(S("symptom"), S("dizziness"), S("migraine")))
metta.space().add_atom(E(S("symptom"), S("anxiety"), S("depression")))
metta.space().add_atom(E(S("symptom"), S("insomnia"), S("depression")))
# Diseases → treatments (one atom per treatment key)
metta.space().add_atom(E(S("treatment"), S("flu"), S("rest")))
metta.space().add_atom(E(S("treatment"), S("flu"), S("fluids")))
metta.space().add_atom(E(S("treatment"), S("flu"), S("antiviral_drugs")))
metta.space().add_atom(E(S("treatment"), S("migraine"), S("pain_relievers")))
metta.space().add_atom(E(S("treatment"), S("migraine"), S("hydration")))
metta.space().add_atom(E(S("treatment"), S("migraine"), S("dark_room")))
metta.space().add_atom(E(S("treatment"), S("depression"), S("therapy")))
metta.space().add_atom(E(S("treatment"), S("depression"), S("antidepressants")))
# Treatments → side effects
metta.space().add_atom(
E(S("side_effect"), S("antiviral_drugs"), ValueAtom("nausea, dizziness"))
)
metta.space().add_atom(
E(S("side_effect"), S("pain_relievers"), ValueAtom("stomach_upset"))
)
metta.space().add_atom(
E(S("side_effect"), S("antidepressants"), ValueAtom("weight_gain, insomnia"))
)
# FAQ keys must match query_faq (not the raw user sentence)
metta.space().add_atom(
E(S("faq"), S("hi"), ValueAtom("Hello! How can I assist you today?"))
)
metta.space().add_atom(
E(
S("faq"),
S("not_a_doctor"),
ValueAtom(
"I am not a doctor. This is a toy demo, not medical advice. "
"See a clinician for diagnosis or treatment."
),
)
)
metta.space().add_atom(
E(
S("faq"),
S("migraine_treatment"),
ValueAtom(
"In this toy graph, migraine treatments include pain_relievers, "
"hydration, and dark_room."
),
)
)
Worked FAQ example: user says Hi → classifier keyword hi → graph key hi → greeting.
Limitation: the classifier extracts one keyword. Sample queries below use a single symptom (for example fever), not “fever and cough”.
Step 2: Implement MeTTa lookup
Create metta/medicalrag.py:
import re
from hyperon import MeTTa, E, S, ValueAtom
SYMBOL_PATTERN = re.compile(r"^[a-z0-9_]+$")
def to_symbol(token: str):
"""Encode multi-word names; reject tokens that would break MeTTa."""
if token is None:
return None
symbol = (
str(token)
.strip()
.strip('"')
.lower()
.replace("'", "")
.replace("\u2019", "")
.replace(" ", "_")
)
if not SYMBOL_PATTERN.fullmatch(symbol):
return None
return symbol
def atom_to_str(atom) -> str:
"""Parse both Symbol and ValueAtom results."""
try:
obj = atom.get_object()
if obj is not None and hasattr(obj, "value"):
return str(obj.value)
except Exception:
pass
return str(atom).strip('"')
class MedicalRAG:
"""MeTTa knowledge lookup (not embedding RAG)."""
def __init__(self, metta_instance: MeTTa):
self.metta = metta_instance
def _run_match(self, relation: str, subject: str):
symbol = to_symbol(subject)
if not symbol:
return []
query_str = f"!(match &self ({relation} {symbol} $x) $x)"
results = self.metta.run(query_str)
if not results:
return []
values = []
for row in results:
if row and len(row) > 0:
values.append(atom_to_str(row[0]))
return list(dict.fromkeys(values))
def query_symptom(self, symptom):
return self._run_match("symptom", symptom)
def get_treatment(self, disease):
return self._run_match("treatment", disease)
def get_side_effects(self, treatment):
return self._run_match("side_effect", treatment)
def query_faq(self, question_or_key):
key = to_symbol(question_or_key)
if not key:
return None
results = self._run_match("faq", key)
return results[0] if results else None
def add_knowledge(self, relation_type, subject, object_value):
rel = to_symbol(relation_type)
subj = to_symbol(subject)
if not rel or not subj or object_value is None:
return "Skipped invalid knowledge"
if rel == "symptom":
obj = to_symbol(object_value)
if not obj:
return "Skipped invalid disease symbol"
atom_obj = S(obj)
elif rel in ("treatment",):
obj = to_symbol(object_value)
if not obj:
return "Skipped invalid treatment symbol"
atom_obj = S(obj)
else:
atom_obj = ValueAtom(str(object_value))
self.metta.space().add_atom(E(S(rel), S(subj), atom_obj))
return f"Added {rel}: {subj} -> {object_value}"
Key Methods:
query_symptom(): Finds diseases for a symbol-safe symptomget_treatment(): Treatment keys for a disease (then look up each key’s side effects)get_side_effects(): Side effects for a treatment key such asantiviral_drugsquery_faq(): FAQ by stable key (hi,migraine_treatment), not the raw sentenceadd_knowledge(): Same atom types as seed data (used only whenLEARN=1)
Step 3: Query processing
Create metta/utils.py. Fallback if not prompt: sits at function scope after all intent branches. Default path does not write LLM output into the graph.
import json
import os
from openai import OpenAI
from .medicalrag import MedicalRAG, to_symbol
DISCLAIMER = (
"Not medical advice. I am not a doctor. This is a toy MeTTa demo, "
"not a substitute for professional care."
)
LEARN = os.getenv("LEARN") == "1"
class LLM:
def __init__(self, api_key):
self.client = OpenAI(
api_key=api_key,
base_url="https://api.asi1.ai/v1",
)
def create_completion(self, prompt, max_tokens=800):
completion = self.client.chat.completions.create(
messages=[{"role": "user", "content": prompt}],
model="asi1",
max_tokens=max_tokens,
)
return completion.choices[0].message.content
def get_intent_and_keyword(query, llm):
"""Agent-side ASI:One call: classify intent and extract one keyword."""
prompt = (
f"Given the query: '{query}'\n"
"Classify the intent as one of: 'symptom', 'treatment', 'side effect', 'faq', or 'unknown'.\n"
"Extract the most relevant single keyword (one symptom, disease, or treatment). "
"Use snake_case for multi-word names (e.g. antiviral_drugs).\n"
"For greetings like Hi/Hello, intent=faq and keyword=hi.\n"
"For 'how do I treat a migraine', intent=faq and keyword=migraine_treatment.\n"
"For 'what's wrong with me', intent=faq and keyword=not_a_doctor.\n"
"Return *only* JSON:\n"
'{ "intent": "<classified_intent>", "keyword": "<extracted_keyword>" }'
)
response = llm.create_completion(prompt)
try:
cleaned = response.strip()
if cleaned.startswith("```"):
cleaned = "\n".join(cleaned.split("\n")[1:])
if cleaned.endswith("```"):
cleaned = "\n".join(cleaned.split("\n")[:-1])
result = json.loads(cleaned.strip())
return result["intent"], result.get("keyword")
except (json.JSONDecodeError, KeyError):
return "unknown", None
def generate_knowledge_response(query, intent, keyword, llm):
"""Optional LLM guess. Do not persist unless LEARN=1."""
if intent == "symptom":
prompt = (
f"Query: '{query}'\n"
f"The symptom '{keyword}' is not in the toy graph. Suggest one plausible disease name "
f"as a snake_case token. Return only that token."
)
elif intent == "treatment":
prompt = (
f"Query: '{query}'\n"
f"No treatments for '{keyword}' in the toy graph. Suggest one snake_case treatment key. "
f"Return only that token."
)
elif intent == "side effect":
prompt = (
f"Query: '{query}'\n"
f"No side effects for '{keyword}'. Suggest a short description. Return only that text."
)
elif intent == "faq":
prompt = (
f"Query: '{query}'\n"
"Provide a concise answer and remind the user this is not medical advice. "
"Return only the answer."
)
else:
return None
return llm.create_completion(prompt)
def _side_effects_for_treatments(rag: MedicalRAG, treatments):
chunks = []
for t in treatments:
effects = rag.get_side_effects(t)
if effects:
chunks.append(f"{t}: {', '.join(effects)}")
return "; ".join(chunks) if chunks else "none in toy graph"
def process_query(query, rag: MedicalRAG, llm: LLM):
intent, keyword = get_intent_and_keyword(query, llm)
keyword = to_symbol(keyword) if keyword else None
prompt = ""
if intent == "faq":
faq_key = keyword or to_symbol(query)
faq_answer = rag.query_faq(faq_key) if faq_key else None
if faq_answer:
prompt = (
f"Query: '{query}'\n"
f"FAQ Answer: '{faq_answer}'\n"
"Humanize this with a friendly tone. Keep the not-a-doctor meaning."
)
else:
new_answer = generate_knowledge_response(query, intent, keyword, llm)
if LEARN and faq_key and new_answer:
rag.add_knowledge("faq", faq_key, new_answer)
prompt = (
f"Query: '{query}'\n"
f"FAQ Answer: '{new_answer}'\n"
"Humanize this with a friendly tone. This is not medical advice."
)
elif intent == "symptom" and keyword:
diseases = rag.query_symptom(keyword)
if not diseases:
disease = generate_knowledge_response(query, intent, keyword, llm)
if LEARN and disease:
rag.add_knowledge("symptom", keyword, disease)
treatments = rag.get_treatment(disease) if disease else []
prompt = (
f"Query: '{query}'\n"
f"Symptom: {keyword}\n"
f"Related Disease (unverified LLM suggestion, not a graph fact): {disease}\n"
f"Treatments in graph: {', '.join(treatments) if treatments else 'none'}\n"
"Be explicit that this is a toy demo, not a diagnosis."
)
else:
disease = diseases[0]
treatments = rag.get_treatment(disease)
side_effects = _side_effects_for_treatments(rag, treatments)
prompt = (
f"Query: '{query}'\n"
f"Symptom: {keyword}\n"
f"Related Disease (toy graph): {disease}\n"
f"Treatments: {', '.join(treatments)}\n"
f"Side Effects: {side_effects}\n"
"Generate a concise, empathetic response. Do not claim to diagnose."
)
elif intent == "treatment" and keyword:
treatments = rag.get_treatment(keyword)
if treatments:
prompt = (
f"Query: '{query}'\n"
f"Disease: {keyword}\n"
f"Treatments: {', '.join(treatments)}\n"
"Provide a helpful suggestion from the toy graph only."
)
else:
treatment = generate_knowledge_response(query, intent, keyword, llm)
if LEARN and treatment:
rag.add_knowledge("treatment", keyword, treatment)
prompt = (
f"Query: '{query}'\n"
f"Disease: {keyword}\n"
f"Treatments (unverified LLM suggestion): {treatment}\n"
"Say this is not from the verified toy graph."
)
elif intent == "side effect" and keyword:
side_effects = rag.get_side_effects(keyword)
if side_effects:
prompt = (
f"Query: '{query}'\n"
f"Treatment: {keyword}\n"
f"Side Effects: {', '.join(side_effects)}\n"
"Explain briefly from the toy graph."
)
else:
side_effect = generate_knowledge_response(query, intent, keyword, llm)
if LEARN and side_effect:
rag.add_knowledge("side_effect", keyword, side_effect)
prompt = (
f"Query: '{query}'\n"
f"Treatment: {keyword}\n"
f"Side Effects (unverified LLM suggestion): {side_effect}\n"
"Do not present this as clinical fact."
)
if not prompt:
prompt = (
f"Query: '{query}'\n"
"No specific info found in the toy graph. Offer general assistance "
"and remind the user to see a clinician."
)
prompt += (
f"\nAlways start the answer with: {DISCLAIMER}\n"
"Then give the helpful content. Do not invent prescriptions."
)
response = llm.create_completion(prompt, max_tokens=800)
text = (response or "").strip()
if DISCLAIMER.lower() not in text.lower():
text = f"{DISCLAIMER}\n\n{text}"
return {"selected_question": query, "humanized_answer": text}
Intent Classification (runs in the agent, after Chat Protocol receives text):
- symptom: one keyword → diseases and treatments in the toy graph
- treatment: treatments for a disease key
- side effect: side effects for a treatment key (
antiviral_drugs,antidepressants) - faq: keyed FAQs (
hi,not_a_doctor,migraine_treatment)
Step 4: Configure Agent
Create agent.py at the project root (not inside metta/):
from datetime import datetime, timezone
from uuid import uuid4
import os
import sys
from dotenv import load_dotenv
from uagents import Context, Protocol, Agent
from hyperon import MeTTa
from uagents_core.contrib.protocols.chat import (
ChatAcknowledgement,
ChatMessage,
EndSessionContent,
StartSessionContent,
TextContent,
chat_protocol_spec,
)
from metta.medicalrag import MedicalRAG
from metta.knowledge import initialize_knowledge_graph
from metta.utils import LLM, process_query, DISCLAIMER
load_dotenv()
api_key = os.getenv("ASI_ONE_API_KEY")
agent_seed = os.getenv("AGENT_SEED")
if not api_key:
print("Missing ASI_ONE_API_KEY. Create a key at https://asi1.ai/dashboard/api-keys and put it in .env")
sys.exit(1)
if not agent_seed:
print("Missing AGENT_SEED. Set a unique local seed in .env (do not commit secrets).")
sys.exit(1)
agent = Agent(
name="Medical MeTTa Agent",
seed=agent_seed,
port=8005,
mailbox=True,
publish_agent_details=True,
)
def create_text_chat(text: str, end_session: bool = False) -> ChatMessage:
content = [TextContent(type="text", text=text)]
if end_session:
content.append(EndSessionContent(type="end-session"))
return ChatMessage(
timestamp=datetime.now(timezone.utc),
msg_id=uuid4(),
content=content,
)
metta = MeTTa()
initialize_knowledge_graph(metta)
rag = MedicalRAG(metta)
llm = LLM(api_key=api_key)
chat_proto = Protocol(spec=chat_protocol_spec)
@chat_proto.on_message(ChatMessage)
async def handle_message(ctx: Context, sender: str, msg: ChatMessage):
ctx.storage.set(str(ctx.session), sender)
await ctx.send(
sender,
ChatAcknowledgement(
timestamp=datetime.now(timezone.utc),
acknowledged_msg_id=msg.msg_id,
),
)
for item in msg.content:
if isinstance(item, StartSessionContent):
ctx.logger.info(f"Got a start session message from {sender}")
continue
elif isinstance(item, TextContent):
user_query = item.text.strip()
ctx.logger.info(f"Got a medical query from {sender}: {user_query}")
try:
response = process_query(user_query, rag, llm)
answer_text = response.get(
"humanized_answer",
f"{DISCLAIMER}\n\nI could not process your query.",
)
await ctx.send(sender, create_text_chat(answer_text))
except Exception as e:
ctx.logger.error(f"Error processing medical query: {e}")
await ctx.send(
sender,
create_text_chat(
f"{DISCLAIMER}\n\nI hit an error processing that query. Please try again."
),
)
else:
ctx.logger.info(f"Got unexpected content from {sender}")
@chat_proto.on_message(ChatAcknowledgement)
async def handle_ack(ctx: Context, sender: str, msg: ChatAcknowledgement):
ctx.logger.info(
f"Got an acknowledgement from {sender} for {msg.acknowledged_msg_id}"
)
agent.include(chat_proto, publish_manifest=True)
if __name__ == "__main__":
agent.run()
Agent Features:
- Toy MeTTa lookup only — not a clinical system or a substitute for care
- Every reply includes a not medical advice / not a doctor disclaimer
- Does not persist unverified LLM output into the graph unless
LEARN=1 - Agent LLM (ASI:One) classifies intent after Chat Protocol receives text
- Compatible with ASI:One via Chat Protocol and Agentverse mailbox (
mailbox=True)
Detailed Working (Step-by-Step)
- User sends a query through ASI:One chat (or Inspector chat).
- Chat Protocol handler receives
TextContent. - The agent calls ASI:One (
get_intent_and_keyword) to classify intent and one keyword. MedicalRAGruns MeTTamatchqueries on the toy graph.- Reply is humanized, disclaimer is prepended, and Chat Protocol sends a string (not a dict).
Testing and Deployment
Local Testing (mailbox)
Numbered steps matching current uAgents + Agentverse. See also Mailbox agents and uAgent creation.
-
Log in to Agentverse.
-
From
project/, with venv active and.envset:python3 agent.pyWindows:
py -3.10 agent.py. -
In the console, copy the inspector URL (it includes your agent address). Expected lines look like:
INFO: [Medical MeTTa Agent]: Starting agent with address: agent1q...
INFO: [Medical MeTTa Agent]: Agent inspector available at https://agentverse.ai/inspect/?uri=http%3A//127.0.0.1%3A8005&address=agent1q...
INFO: [Medical MeTTa Agent]: Starting mailbox client for https://agentverse.ai
INFO: [mailbox]: Successfully registered as mailbox agent in AgentverseIf you see
Missing ASI_ONE_API_KEY, stop and fix.env— the agent exits beforeagent.run(). -
Open the inspector URL while logged in. Choose Connect → Mailbox (Agentverse issues the mailbox token; you do not paste Python
import mailbox). -
Use Chat with Agent on the Inspector/profile, or continue to ASI:One below. Keep
agent.pyrunning.
Sample queries (aligned with one-keyword lookup)
Hi→ FAQ keyhi(greeting)I have a fever, what could this indicate?→ symptomfever→ toy diseaseflu→rest,fluids,antiviral_drugs(side effects onantiviral_drugs)What treatment is commonly used for migraine?→ treatments formigraineWhat side effects can antidepressants have?→ side effects forantidepressantsHow do I treat a migraine?→ FAQ keymigraine_treatment

Query your agent from ASI:One
ASI:One discovers mailbox agents that are running, registered, and using Chat Protocol. README/handle tips: Searching agents. Chat UI: ASI:One Chat.
- Copy the agent address from the console (
agent1q...). Optionally set a handle on the Agentverse profile. - Open ASI:One, sign in with Google or the ASI:One wallet, and start a new chat.
- Toggle Agents so ASI:One can call Agentverse agents.
- Paste the address or
@handleand send a sample query such asI have a fever, what could this indicate? - Expect a reply that starts with the not-a-doctor disclaimer and mentions the toy flu graph (not a real diagnosis). The local console should log the incoming chat message.

Expected output
Startup (shape of logs; address is unique to your AGENT_SEED):
INFO: [Medical MeTTa Agent]: Starting agent with address: agent1q...
INFO: [Medical MeTTa Agent]: Agent inspector available at https://agentverse.ai/inspect/?uri=...
INFO: [mailbox]: Successfully registered as mailbox agent in Agentverse
Example chat:
- You:
I have a fever, what could this indicate? - Agent: Starts with Not medical advice. I am not a doctor... then, from the toy graph, links
fever→fluand listsrest,fluids,antiviral_drugs(with nausea/dizziness onantiviral_drugsif asked).