Skip to content

Commit 3e63789

Browse files
authored
Merge pull request #133 from andrewm4894/add-anomaly-agent
Add anomaly agent
2 parents 069f3d1 + f3aa3ad commit 3e63789

File tree

10 files changed

+76
-870
lines changed

10 files changed

+76
-870
lines changed

.example.env

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,15 @@ ANOMSTACK_DAGSTER_SQLITE_STORAGE_BASE_DIR=tmp
7373

7474
# OpenAI env vars for LLM based alerts
7575
ANOMSTACK_OPENAI_KEY=
76+
OPENAI_API_KEY=
7677
ANOMSTACK_OPENAI_MODEL=gpt-4o-mini
7778

79+
# langsmith related env vars
80+
LANGSMITH_TRACING=true
81+
LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
82+
LANGSMITH_API_KEY=""
83+
LANGSMITH_PROJECT="anomaly-agent"
84+
7885
# Anthropic env vars for LLM based alerts
7986
ANOMSTACK_ANTHROPIC_KEY=
8087
ANOMSTACK_ANTHROPIC_MODEL=claude-3-haiku-20240307

README.md

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Painless open source anomaly detection for your metrics! 📈📉🚀
3636
- [Visualization](#visualization)
3737
- [Concepts](#concepts)
3838
- [Alerts](#alerts)
39-
- [LLM Alerts](#llm-alerts)
39+
- [LLM Agent Alerts](#llm-agent-alerts)
4040
- [Contributing](#contributing)
4141

4242
Supported sources and databases for your metrics to live in and be queried from:
@@ -172,7 +172,7 @@ Here is a list of features of Anomstack (emoji alert warning!)
172172
5. 🛠️ - Ability to define your own custom python preprocess function instead of the default at [`/metrics/defaults/python/preprocess.py`](./metrics/defaults/python/preprocess.py).
173173
6. 📧 - Email [alerting](#alerts) with fancy(ish) ascii art plots of your metrics and anomaly scores.
174174
7. 💬 - Slack alerts too (want to make these nicer).
175-
8. 🤖 - LLM based alerts ([OpenAI](./anomstack/llm/openai.py) & [Anthropic](./anomstack/llm/anthropic.py)) - see [LLM Alerts](#llm-alerts). p.s. they don't work great yet - experimental :)
175+
8. 🤖 - aGeNtIc LLM based alerts - use an [anomaly-agent](https://github.com/andrewm4894/anomaly-agent) to do anomaly detection and alerting - see [LLM Agent Alerts](#llm-agent-alerts).
176176
9. 🕒 - Ability to ingest at whatever frequency you want and then agg to a different level for training/scoring, see [`freq`](/metrics/examples/freq/README.md) example.
177177
10. 📊 - Plot jobs so you can just eyeball your metrics in Dagster job logs, see [#dagster-ui-plots](#dagster-ui-plots).
178178
11. 🏗️ - Minimal infrastructure requirements, Anomstack just reads from and writes to whatever database you use.
@@ -557,21 +557,18 @@ Below is an example of an alert via email. Attached is a png plot with more deta
557557

558558
![plot](./docs/img/random_1.png)
559559

560-
## LLM Alerts
560+
## LLM Agent Alerts
561561

562562
[back to top](#anomstack)
563563

564-
Yes! I have managed to find a way to ram a large language model (LLM) into this project. But you know what, it might just work...
564+
Yes! I have managed to find a way to ram a large language (LLM) ~~model~~ 🚀AGENT🚀 into this project.
565565

566-
~~**Update**: It works horribly, but it works! 🤣. Still need to do a lot more prompt engineering to get this to work well, but it's a start.~~
567-
568-
**Update Update**: I know how to make this work much better and more reliable + latest models are better - going to refactor this soon (done [here](https://github.com/andrewm4894/anomstack/pull/127)).
569-
570-
Idea here is to just send the metric data and prompt to a LLM (ChatGPT) and ask it if it thinks the metric looks anomalous (and provide back an explanation). If it does, we alert.
566+
Idea here is to just send the metric data and prompt to an LLM Agent (built with the [anomaly-agent](https://github.com/andrewm4894/anomaly-agent)) and ask it if it thinks the metric looks anomalous (and run a verification chain to check it and also provide back an explanation for each anomaly). If it does, we alert.
571567

572568
Notes:
573569
- If you don't want to send your metric data to OpenAI then just set `disable_llmalert` to `True` in your metric batch config.
574-
- Support for Anthropic models added [here](https://github.com/andrewm4894/anomstack/pull/128)
570+
- Going to add and validate [Ollama](https://ollama.com/) support soon for local LLM Agent.
571+
- Will be developing this more in its own library (for use in both Anomstack and any other projects) over at [anomaly-agent](https://github.com/andrewm4894/anomaly-agent) dig in there for more details.
575572

576573
<details>
577574
<summary>Click to see some LLM Alert screenshots</summary>

anomstack/jobs/llmalert.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from anomstack.df.wrangle import wrangle_df
2222
from anomstack.fn.run import define_fn
2323
from anomstack.jinja.render import render
24-
from anomstack.llm.detect import detect_anomalies
24+
from anomstack.llm.agent import detect_anomalies
2525
from anomstack.sql.read import read_sql
2626
from anomstack.validate.validate import validate_df
2727

@@ -135,20 +135,22 @@ def llmalert(context, df: pd.DataFrame) -> pd.DataFrame:
135135
if llmalert_metric_rounding >= 0:
136136
df_prompt = df_prompt.round(llmalert_metric_rounding)
137137

138-
# logger.debug(f"df_prompt: \n{df_prompt}")
139-
140-
prompt = make_prompt(df_prompt)
141-
142-
# logger.debug(f"prompt: \n{prompt}")
143-
144-
detected_anomalies = detect_anomalies(prompt)
145-
df_detected_anomalies = pd.DataFrame(detected_anomalies)
138+
df_detected_anomalies = detect_anomalies(df_prompt)
139+
df_detected_anomalies = df_detected_anomalies.rename(
140+
columns={
141+
"metric_timestamp": "anomaly_timestamp",
142+
"anomaly_description": "anomaly_explanation"
143+
}
144+
)
146145

147146
num_anomalies_total = len(df_detected_anomalies)
148147
logger.debug(f"{num_anomalies_total} total anomalies detected in {metric_name}")
149148

150149
# ensure both columns are datetime64[ns] type before merging
151150
df_metric["metric_timestamp"] = df_metric["metric_timestamp"].dt.strftime("%Y-%m-%d %H:%M:%S")
151+
df_detected_anomalies["anomaly_timestamp"] = df_detected_anomalies["anomaly_timestamp"].dt.strftime("%Y-%m-%d %H:%M:%S")
152+
153+
# merge the two dataframes on the metric_timestamp column
152154
df_metric = df_metric.merge(
153155
df_detected_anomalies,
154156
how="left",

anomstack/llm/agent.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import pandas as pd
2+
3+
from anomaly_agent import AnomalyAgent
4+
5+
6+
def detect_anomalies(df: pd.DataFrame) -> pd.DataFrame:
7+
"""
8+
Detect anomalies using the AnomalyAgent.
9+
10+
Args:
11+
df (pd.DataFrame): The input DataFrame containing metric data.
12+
13+
Returns:
14+
pd.DataFrame: A DataFrame containing the detected anomalies.
15+
"""
16+
anomaly_agent = AnomalyAgent()
17+
anomalies = anomaly_agent.detect_anomalies(df, timestamp_col="metric_timestamp")
18+
df_anomalies = anomaly_agent.get_anomalies_df(anomalies)
19+
20+
return df_anomalies

anomstack/llm/anthropic.py

Lines changed: 0 additions & 89 deletions
This file was deleted.

anomstack/llm/detect.py

Lines changed: 0 additions & 24 deletions
This file was deleted.

anomstack/llm/models.py

Lines changed: 0 additions & 22 deletions
This file was deleted.

anomstack/llm/openai.py

Lines changed: 0 additions & 83 deletions
This file was deleted.

requirements.compile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
anthropic
1+
anomaly-agent
22
boto3
33
dagit
44
dagster
@@ -17,7 +17,6 @@ libsql-experimental
1717
matplotlib
1818
numpy
1919
oscrypto
20-
openai
2120
pandas
2221
pandas-gbq
2322
plotly

0 commit comments

Comments
 (0)