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]>
66 lines
1.8 KiB
Rust
66 lines
1.8 KiB
Rust
//! 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);
|
|
}
|
|
}
|