Add Maritime/Logistics support assistant with safety/eval layer
RAG + multi-agent (retrieval, policy-compliance, drafting) support assistant for a fictional shipping company, built in Rust with Qdrant for vector storage and a hand-rolled Anthropic tool-calling client (no Rust pydantic-ai equivalent exists). A deterministic supervisor orchestrates the agents and runs every response through a rule-based guardrail pipeline with an append-only audit log. Includes a 44-example golden dataset (normal/injection/policy- violation/hallucination-bait/out-of-scope) and a regression harness that scores retrieval, generation, and guardrail correctness separately, plus a write-up of the failure modes and trade-offs. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
//! Deterministic extraction of shipment identifiers from free text.
|
||||
//!
|
||||
//! Pulled out as its own unit (rather than a method on `DocumentRetriever`)
|
||||
//! purely so it's testable without spinning up a Qdrant connection.
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
pub struct ReferenceExtractor {
|
||||
booking_reference_re: Regex,
|
||||
container_number_re: Regex,
|
||||
}
|
||||
|
||||
impl ReferenceExtractor {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
booking_reference_re: Regex::new(r"\bMSL-[A-Z0-9]{7}\b").expect("valid regex"),
|
||||
container_number_re: Regex::new(r"\b[A-Z]{4}\d{7}\b").expect("valid regex"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Preferred over asking the LLM to do this: it's cheap, exact, and
|
||||
/// never hallucinates a reference that wasn't actually typed.
|
||||
pub fn extract(&self, query: &str) -> Option<String> {
|
||||
let upper = query.to_uppercase();
|
||||
self.booking_reference_re
|
||||
.find(&upper)
|
||||
.or_else(|| self.container_number_re.find(&upper))
|
||||
.map(|m| m.as_str().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ReferenceExtractor {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn extracts_booking_reference_case_insensitively() {
|
||||
let r = ReferenceExtractor::new();
|
||||
assert_eq!(
|
||||
r.extract("where is msl-4b7k921 right now?"),
|
||||
Some("MSL-4B7K921".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_container_number() {
|
||||
let r = ReferenceExtractor::new();
|
||||
assert_eq!(
|
||||
r.extract("container MSCU1234567 status please"),
|
||||
Some("MSCU1234567".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_none_when_no_reference_present() {
|
||||
let r = ReferenceExtractor::new();
|
||||
assert_eq!(r.extract("what are your office hours?"), None);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user