dhrumilparikh's picture
Round 2 final submission — Fleet AI Oversight v2.0
1a4574d
Raw
History Blame Contribute Delete
11.5 kB
"""
Worker Agent 3 — Embedding Agent
Task: Embed chunked documents into a vector store using selected model.
Uses mock embeddings for determinism during training.
"""
from __future__ import annotations
import hashlib
import json
import math
from pathlib import Path
from typing import Any, Optional
from workers.base_worker import BaseWorker
EMBEDDING_TASK_CONFIGS = {
"easy_embedding": {"task_id": "easy_embedding", "difficulty": "easy", "step_budget": 8, "corpus_size": 50},
"medium_embedding": {"task_id": "medium_embedding", "difficulty": "medium", "step_budget": 10, "corpus_size": 100},
"hard_embedding": {"task_id": "hard_embedding", "difficulty": "hard", "step_budget": 12, "corpus_size": 200},
}
VALID_MODELS = ["all-MiniLM-L6-v2", "all-mpnet-base-v2", "paraphrase-multilingual-MiniLM-L12-v2"]
EMBEDDING_DIM = 384
STOP_WORDS = {"does", "is", "a", "the", "does", "support", "nexacrm", "faq", "what", "are", "you", "built", "on", "with", "how", "do", "i", "can", "an", "available", "on", "for", "in", "at", "rest"}
def mock_embed(text: str) -> list[float]:
"""
Improved keyword-overlap embedding with stop-word filtering.
Prioritizes unique terms like 'Slack', 'Pricing', or 'GDPR'.
"""
# Tokenization and filtering
raw_words = text.lower().replace("?", "").replace("!", "").replace(".", "").replace(",", "").replace("\u2014", " ").split()
words = [w for w in raw_words if w not in STOP_WORDS and len(w) > 1]
if not words:
# Fallback to all words if everything was a stopword
words = [w for w in raw_words if len(w) > 1]
if not words:
return [0.0] * EMBEDDING_DIM
vec = [0.0] * EMBEDDING_DIM
for w in words:
idx = int(hashlib.md5(w.encode()).hexdigest(), 16) % EMBEDDING_DIM
# Keywords get higher weight
vec[idx] += 1.0
norm = math.sqrt(sum(x * x for x in vec)) or 1.0
return [x / norm for x in vec]
class EmbeddingEnv(BaseWorker):
"""
Worker Agent 3: Embedding Agent.
Actions:
- select_model: Choose embedding model
- configure_batch_size: Set batch size for embedding
- run_embedding: Embed all chunks
- validate_coverage: Check coverage ratio
- inspect_vectors: Sample 3 vectors
- store_index: Persist index to memory
- submit: Submit index and receive terminal reward
"""
VALID_ACTIONS = [
"select_model", "configure_batch_size", "run_embedding",
"validate_coverage", "inspect_vectors", "store_index", "submit"
]
def __init__(self) -> None:
super().__init__(worker_id="worker_3", worker_name="Embedding Agent")
self.selected_model: Optional[str] = None
self.batch_size: int = 32
self.index: dict[str, list[float]] = {} # chunk_id -> vector
self.chunks_embedded: int = 0
self.null_embedding_count: int = 0
self.coverage_ratio: float = 0.0
self.index_stored: bool = False
self.embedding_done: bool = False
self.task_config: dict = {}
self.input_chunks: list[dict] = []
def reset(self, task_id: str, input_chunks: Optional[list[dict]] = None) -> dict:
self._reset_episode_tracking()
self.task_id = task_id
self.task_config = EMBEDDING_TASK_CONFIGS.get(task_id, EMBEDDING_TASK_CONFIGS["easy_embedding"])
self.step_budget = self.task_config["step_budget"]
self.step_budget_remaining = self.step_budget
self.selected_model = None
self.batch_size = 32
self.index = {}
self.chunks_embedded = 0
self.null_embedding_count = 0
self.coverage_ratio = 0.0
self.index_stored = False
self.embedding_done = False
self.input_chunks = input_chunks or self._load_chunks(self.task_config["corpus_size"])
return self.state()
def step(self, action_dict: dict) -> tuple[dict, float, bool, dict]:
operation = action_dict.get("operation", "")
parameters = action_dict.get("parameters", {})
if self.is_done:
return self.state(), 0.0, True, {"error": "episode_already_done", "action": operation}
if operation not in self.VALID_ACTIONS:
self._record_governance_event("invalid_action", "medium", f"Unknown: {operation}")
reward = self._clip_reward(0.0)
self.step_count += 1
self.step_budget_remaining -= 1
self.total_reward += reward
done = self._is_budget_exhausted()
if done:
self.is_done = True
info = {"error": f"invalid_action:{operation}", "action": operation}
self._record_action(operation, reward, info)
return self.state(), reward, done, info
reward, info = self._dispatch(operation, parameters)
reward = self._clip_reward(reward)
self.step_count += 1
self.step_budget_remaining -= 1
self.total_reward += reward
done = self.is_done or self._is_budget_exhausted()
if done:
self.is_done = True
self._record_action(operation, reward, info)
return self.state(), reward, done, info
def state(self) -> dict:
base = self._get_base_state()
base.update({
"selected_model": self.selected_model,
"batch_size": self.batch_size,
"chunks_embedded": self.chunks_embedded,
"null_embedding_count": self.null_embedding_count,
"coverage_ratio": round(self.coverage_ratio, 4),
"index_stored": self.index_stored,
"embedding_done": self.embedding_done,
"input_chunk_count": len(self.input_chunks),
})
return base
def generate_run_report(self) -> dict:
report = self._get_base_report()
report.update({
"selected_model": self.selected_model,
"chunks_embedded": self.chunks_embedded,
"null_embedding_count": self.null_embedding_count,
"coverage_ratio": self.coverage_ratio,
"index_stored": self.index_stored,
"final_score": self._compute_final_score(),
})
return report
def evaluate_run(self) -> dict:
epsilon = 1e-6
score = min(max(self._compute_final_score(), epsilon), 1.0 - epsilon)
gates = {
"model_selected": self.selected_model is not None,
"embedding_executed": self.embedding_done,
"index_stored": self.index_stored,
"no_null_embeddings": self.null_embedding_count == 0,
"submitted": self.submitted,
}
return {"approved": all(gates.values()) and score >= 0.6, "gates": gates, "composite_score": score}
def _dispatch(self, operation: str, parameters: dict) -> tuple[float, dict]:
if operation == "select_model":
return self._select_model(parameters)
elif operation == "configure_batch_size":
return self._configure_batch_size(parameters)
elif operation == "run_embedding":
return self._run_embedding()
elif operation == "validate_coverage":
return self._validate_coverage()
elif operation == "inspect_vectors":
return self._inspect_vectors()
elif operation == "store_index":
return self._store_index()
elif operation == "submit":
return self._submit()
return 0.0, {"error": "unknown_operation"}
def _select_model(self, params: dict) -> tuple[float, dict]:
model = params.get("model_name", "")
if model not in VALID_MODELS:
return 0.1, {"error": f"invalid_model:{model}", "action": "select_model"}
self.selected_model = model
return 0.4, {"error": None, "action": "select_model", "model": model}
def _configure_batch_size(self, params: dict) -> tuple[float, dict]:
try:
bs = int(params.get("batch_size", 32))
self.batch_size = max(1, min(bs, 256))
return 0.2, {"error": None, "action": "configure_batch_size", "batch_size": self.batch_size}
except (ValueError, TypeError):
return 0.0, {"error": "invalid_batch_size", "action": "configure_batch_size"}
def _run_embedding(self) -> tuple[float, dict]:
if not self.selected_model:
self._record_governance_event("no_model", "high", "run_embedding called without model selected")
return 0.05, {"error": "model_not_selected", "action": "run_embedding"}
self.index = {}
self.null_embedding_count = 0
for chunk in self.input_chunks:
cid = chunk.get("chunk_id", f"chunk_{len(self.index):03d}")
text = chunk.get("text", "")
if not text.strip():
self.null_embedding_count += 1
continue
self.index[cid] = mock_embed(text)
self.chunks_embedded = len(self.index)
total = len(self.input_chunks)
self.coverage_ratio = self.chunks_embedded / max(total, 1)
self.embedding_done = True
reward = 0.4 * self.coverage_ratio + 0.3 * (1.0 if self.null_embedding_count == 0 else 0.3) + 0.2 * (self.step_budget_remaining / max(self.step_budget, 1))
return reward, {"error": None, "action": "run_embedding", "chunks_embedded": self.chunks_embedded}
def _validate_coverage(self) -> tuple[float, dict]:
if not self.embedding_done:
return 0.1, {"error": "embedding_not_done", "action": "validate_coverage"}
report = {"coverage_ratio": round(self.coverage_ratio, 4), "null_count": self.null_embedding_count, "total_chunks": len(self.input_chunks)}
reward = 0.5 if self.coverage_ratio >= 0.95 else 0.2
return reward, {"error": None, "action": "validate_coverage", "report": report}
def _inspect_vectors(self) -> tuple[float, dict]:
sample = {k: v[:5] for k, v in list(self.index.items())[:3]}
return 0.15, {"error": None, "action": "inspect_vectors", "sample": sample}
def _store_index(self) -> tuple[float, dict]:
if not self.embedding_done:
return 0.05, {"error": "embedding_not_done", "action": "store_index"}
self.index_stored = True
return 0.4, {"error": None, "action": "store_index", "index_size": len(self.index)}
def _submit(self) -> tuple[float, dict]:
if not self.index_stored:
self._record_governance_event("premature_submit", "high", "submit called before store_index")
return 0.05, {"error": "index_not_stored", "action": "submit"}
self.submitted = True
self.is_done = True
final_score = self._compute_final_score()
return min(final_score + 0.15, 0.99), {"error": None, "action": "submit", "final_score": final_score}
def _compute_final_score(self) -> float:
if not self.embedding_done:
return 0.001
score = (
0.4 * self.coverage_ratio +
0.3 * (1.0 if self.null_embedding_count == 0 else 0.0) +
0.2 * (1.0 if self.index_stored else 0.0) +
0.1 * (self.step_budget_remaining / max(self.step_budget, 1))
)
return self._clip_reward(score)
def _load_chunks(self, size: int) -> list[dict]:
path = Path("data/nexacrm_corpus.json")
if path.exists():
with open(path) as f:
return json.load(f)[:size]
return [{"chunk_id": f"chunk_{i:03d}", "text": f"NexaCRM entry {i}"} for i in range(size)]