1
0

Presentation_Technical.html 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Local Food AI - Capstone Technical Presentation</title>
  7. <!-- Google Fonts -->
  8. <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700&family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
  9. <style>
  10. :root {
  11. --bg-color: #0b0c10;
  12. --slide-bg: rgba(21, 26, 35, 0.7);
  13. --border-color: rgba(255, 255, 255, 0.08);
  14. --primary-accent: #58a6ff;
  15. --secondary-accent: #bc8cff;
  16. --text-primary: #e6edf3;
  17. --text-secondary: #8b949e;
  18. --text-muted: #484f58;
  19. --code-bg: #161b22;
  20. --font-display: 'Outfit', sans-serif;
  21. --font-sans: 'Inter', sans-serif;
  22. --font-mono: 'Fira Code', monospace;
  23. --glow-color: rgba(88, 166, 255, 0.15);
  24. }
  25. * {
  26. box-sizing: border-box;
  27. margin: 0;
  28. padding: 0;
  29. }
  30. body {
  31. background-color: var(--bg-color);
  32. background-image:
  33. radial-gradient(at 0% 0%, rgba(88, 166, 255, 0.08) 0, transparent 50%),
  34. radial-gradient(at 100% 100%, rgba(188, 140, 255, 0.08) 0, transparent 50%);
  35. color: var(--text-primary);
  36. font-family: var(--font-sans);
  37. overflow: hidden;
  38. height: 100vh;
  39. width: 100vw;
  40. display: flex;
  41. flex-direction: column;
  42. justify-content: center;
  43. align-items: center;
  44. }
  45. /* Slide Container */
  46. .presentation-container {
  47. position: relative;
  48. width: 90%;
  49. max-width: 1100px;
  50. height: 80vh;
  51. min-height: 550px;
  52. perspective: 1500px;
  53. }
  54. .slide-deck {
  55. position: relative;
  56. width: 100%;
  57. height: 100%;
  58. transform-style: preserve-3d;
  59. }
  60. /* Individual Slides */
  61. .slide {
  62. position: absolute;
  63. width: 100%;
  64. height: 100%;
  65. background: var(--slide-bg);
  66. border: 1px solid var(--border-color);
  67. border-radius: 16px;
  68. padding: 3.5rem;
  69. backdrop-filter: blur(12px);
  70. -webkit-backdrop-filter: blur(12px);
  71. box-shadow: 0 20px 50px rgba(0, 0, 0, 0.4), inset 0 1px 1px rgba(255, 255, 255, 0.1);
  72. display: flex;
  73. flex-direction: column;
  74. justify-content: space-between;
  75. opacity: 0;
  76. visibility: hidden;
  77. transform: scale(0.9) translateZ(-100px);
  78. transition: all 0.6s cubic-bezier(0.25, 0.8, 0.25, 1);
  79. pointer-events: none;
  80. }
  81. /* Slide States for Animations */
  82. .slide.active {
  83. opacity: 1;
  84. visibility: visible;
  85. transform: scale(1) translateZ(0);
  86. pointer-events: auto;
  87. z-index: 10;
  88. }
  89. .slide.prev {
  90. opacity: 0;
  91. visibility: hidden;
  92. transform: scale(0.9) translateX(-100%) rotateY(15deg);
  93. z-index: 5;
  94. }
  95. .slide.next {
  96. opacity: 0;
  97. visibility: hidden;
  98. transform: scale(0.9) translateX(100%) rotateY(-15deg);
  99. z-index: 5;
  100. }
  101. /* Content Formatting */
  102. .slide-header {
  103. display: flex;
  104. justify-content: space-between;
  105. align-items: center;
  106. border-bottom: 1px dashed var(--border-color);
  107. padding-bottom: 1rem;
  108. margin-bottom: 1.5rem;
  109. }
  110. .slide-header .tag {
  111. font-family: var(--font-mono);
  112. font-size: 0.8rem;
  113. color: var(--secondary-accent);
  114. text-transform: uppercase;
  115. letter-spacing: 2px;
  116. }
  117. .slide-header .title-badge {
  118. font-size: 0.75rem;
  119. padding: 0.25rem 0.75rem;
  120. border-radius: 20px;
  121. background: rgba(88, 166, 255, 0.1);
  122. border: 1px solid rgba(88, 166, 255, 0.2);
  123. color: var(--primary-accent);
  124. font-family: var(--font-mono);
  125. }
  126. .slide-body {
  127. flex-grow: 1;
  128. display: flex;
  129. flex-direction: column;
  130. justify-content: center;
  131. }
  132. .slide-title {
  133. font-family: var(--font-display);
  134. font-size: 2.2rem;
  135. font-weight: 700;
  136. line-height: 1.2;
  137. margin-bottom: 1.5rem;
  138. background: linear-gradient(135deg, var(--primary-accent), var(--secondary-accent));
  139. -webkit-background-clip: text;
  140. -webkit-text-fill-color: transparent;
  141. }
  142. .slide-footer {
  143. display: flex;
  144. justify-content: space-between;
  145. align-items: center;
  146. border-top: 1px solid var(--border-color);
  147. padding-top: 1.2rem;
  148. margin-top: 1.5rem;
  149. font-size: 0.8rem;
  150. color: var(--text-secondary);
  151. }
  152. /* Grid Layouts inside slides */
  153. .slide-grid-2 {
  154. display: grid;
  155. grid-template-columns: 1fr 1fr;
  156. gap: 2rem;
  157. align-items: start;
  158. }
  159. .slide-grid-3 {
  160. display: grid;
  161. grid-template-columns: 1fr 1fr 1fr;
  162. gap: 1.5rem;
  163. }
  164. /* Lists and Cards */
  165. ul {
  166. list-style: none;
  167. }
  168. li {
  169. position: relative;
  170. padding-left: 1.5rem;
  171. margin-bottom: 0.8rem;
  172. font-size: 1.05rem;
  173. line-height: 1.5;
  174. color: var(--text-primary);
  175. }
  176. li::before {
  177. content: "→";
  178. position: absolute;
  179. left: 0;
  180. color: var(--primary-accent);
  181. font-weight: bold;
  182. }
  183. li strong {
  184. color: #fff;
  185. font-weight: 600;
  186. }
  187. .card {
  188. background: rgba(255, 255, 255, 0.02);
  189. border: 1px solid rgba(255, 255, 255, 0.05);
  190. border-radius: 8px;
  191. padding: 1.2rem;
  192. transition: all 0.3s ease;
  193. }
  194. .card:hover {
  195. background: rgba(255, 255, 255, 0.04);
  196. border-color: rgba(88, 166, 255, 0.2);
  197. box-shadow: 0 4px 20px var(--glow-color);
  198. transform: translateY(-2px);
  199. }
  200. .card-title {
  201. font-family: var(--font-display);
  202. font-size: 1.15rem;
  203. font-weight: 600;
  204. color: #fff;
  205. margin-bottom: 0.6rem;
  206. display: flex;
  207. align-items: center;
  208. gap: 0.5rem;
  209. }
  210. .card-title .icon {
  211. color: var(--primary-accent);
  212. }
  213. .card-desc {
  214. font-size: 0.95rem;
  215. color: var(--text-secondary);
  216. line-height: 1.4;
  217. }
  218. /* Title Slide Layout */
  219. .title-slide {
  220. justify-content: center;
  221. align-items: center;
  222. text-align: center;
  223. }
  224. .title-slide .slide-title {
  225. font-size: 3.2rem;
  226. margin-bottom: 1.5rem;
  227. }
  228. .title-slide .subtitle {
  229. font-size: 1.3rem;
  230. color: var(--text-secondary);
  231. margin-bottom: 2rem;
  232. max-width: 700px;
  233. }
  234. .meta-grid {
  235. display: grid;
  236. grid-template-columns: repeat(3, 160px);
  237. gap: 1.5rem;
  238. margin-top: 2rem;
  239. border-top: 1px solid var(--border-color);
  240. padding-top: 2rem;
  241. }
  242. .meta-item {
  243. display: flex;
  244. flex-direction: column;
  245. align-items: center;
  246. }
  247. .meta-label {
  248. font-size: 0.75rem;
  249. text-transform: uppercase;
  250. letter-spacing: 1px;
  251. color: var(--text-secondary);
  252. margin-bottom: 0.4rem;
  253. }
  254. .meta-value {
  255. font-size: 0.95rem;
  256. font-weight: 500;
  257. color: #fff;
  258. }
  259. /* Code Block Styling */
  260. pre {
  261. background: var(--code-bg);
  262. border: 1px solid var(--border-color);
  263. border-radius: 8px;
  264. padding: 1rem;
  265. overflow: auto;
  266. max-height: 250px;
  267. text-align: left;
  268. }
  269. code {
  270. font-family: var(--font-mono);
  271. font-size: 0.9rem;
  272. color: #ff7b72;
  273. }
  274. pre code {
  275. color: #e6edf3;
  276. }
  277. .highlight-text {
  278. color: var(--primary-accent);
  279. font-weight: 500;
  280. }
  281. /* SVG Schematic Layout */
  282. .architecture-svg {
  283. width: 100%;
  284. height: 260px;
  285. background: rgba(0, 0, 0, 0.2);
  286. border: 1px solid var(--border-color);
  287. border-radius: 8px;
  288. padding: 0.5rem;
  289. }
  290. /* Navigation Controls */
  291. .controls {
  292. position: absolute;
  293. bottom: 2.5vh;
  294. display: flex;
  295. align-items: center;
  296. gap: 2rem;
  297. z-index: 100;
  298. }
  299. .nav-btn {
  300. background: rgba(255, 255, 255, 0.05);
  301. border: 1px solid var(--border-color);
  302. border-radius: 50%;
  303. width: 50px;
  304. height: 50px;
  305. color: var(--text-primary);
  306. cursor: pointer;
  307. display: flex;
  308. justify-content: center;
  309. align-items: center;
  310. font-size: 1.2rem;
  311. transition: all 0.2s ease;
  312. }
  313. .nav-btn:hover:not(:disabled) {
  314. background: rgba(88, 166, 255, 0.2);
  315. border-color: var(--primary-accent);
  316. box-shadow: 0 0 15px var(--glow-color);
  317. color: #fff;
  318. transform: scale(1.05);
  319. }
  320. .nav-btn:disabled {
  321. opacity: 0.3;
  322. cursor: not-allowed;
  323. }
  324. .progress-bar-container {
  325. width: 250px;
  326. height: 6px;
  327. background: rgba(255, 255, 255, 0.05);
  328. border-radius: 3px;
  329. overflow: hidden;
  330. }
  331. .progress-bar {
  332. height: 100%;
  333. width: 0%;
  334. background: linear-gradient(90deg, var(--primary-accent), var(--secondary-accent));
  335. border-radius: 3px;
  336. transition: width 0.4s ease;
  337. }
  338. .slide-counter {
  339. font-family: var(--font-mono);
  340. font-size: 0.85rem;
  341. color: var(--text-secondary);
  342. min-width: 100px;
  343. text-align: center;
  344. }
  345. /* Dynamic SVG diagram styles */
  346. .svg-box { fill: #161b22; stroke: var(--border-color); stroke-width: 1.5; rx: 6; }
  347. .svg-box:hover { fill: #21262d; stroke: var(--primary-accent); }
  348. .svg-text { fill: #e6edf3; font-family: var(--font-sans); font-size: 12px; text-anchor: middle; font-weight: 500; }
  349. .svg-text-sub { fill: var(--text-secondary); font-family: var(--font-mono); font-size: 10px; text-anchor: middle; }
  350. .svg-line { stroke: var(--text-secondary); stroke-width: 1.5; stroke-dasharray: 4; fill: none; }
  351. .svg-line-active { stroke: var(--primary-accent); stroke-width: 2; fill: none; }
  352. </style>
  353. </head>
  354. <body>
  355. <div class="presentation-container">
  356. <div class="slide-deck">
  357. <!-- Slide 1: Title -->
  358. <div class="slide title-slide active">
  359. <div class="slide-header">
  360. <span class="tag">Local Food AI</span>
  361. <span class="title-badge">CAPSTONE PROJECT</span>
  362. </div>
  363. <div class="slide-body">
  364. <h1 class="slide-title">Local Food AI Platform</h1>
  365. <p class="subtitle">Complete Technical Architecture, Development Lifecycle, and Security Governance Overview</p>
  366. <div class="meta-grid">
  367. <div class="meta-item">
  368. <span class="meta-label">Audience</span>
  369. <span class="meta-value">IT & Technical Team</span>
  370. </div>
  371. <div class="meta-item">
  372. <span class="meta-label">Lead Engineer</span>
  373. <span class="meta-value">Francois Lange</span>
  374. </div>
  375. <div class="meta-item">
  376. <span class="meta-label">Version ID</span>
  377. <span class="meta-value">2a8ed056 (main)</span>
  378. </div>
  379. </div>
  380. </div>
  381. <div class="slide-footer">
  382. <span>Department of Domain-specific Project 1 (DSP1) & PM1</span>
  383. <span>1 / 11</span>
  384. </div>
  385. </div>
  386. <!-- Slide 2: Core IT Requirements -->
  387. <div class="slide">
  388. <div class="slide-header">
  389. <span class="tag">System Design</span>
  390. <span class="title-badge">IT REQUIREMENTS</span>
  391. </div>
  392. <div class="slide-body">
  393. <h2 class="slide-title">Core Requirements Analysis & Assessment</h2>
  394. <div class="slide-grid-2">
  395. <div>
  396. <ul>
  397. <li><strong>100% Privacy & Data Sovereignty</strong>: Absolute enforcement of an air-gapped system. Patient parameters, query strings, and custom food plans must never escape the local subnet.</li>
  398. <li><strong>Sub-second Database Response</strong>: Fast SQL lookup queries against the 24GB OpenFoodFacts dataset (with over 100+ raw schema parameters).</li>
  399. <li><strong>Offline LLM Inference Capabilities</strong>: Local AI reasoning model supporting clinical tool-calling without internet-based APIs.</li>
  400. </ul>
  401. </div>
  402. <div class="slide-grid-2" style="grid-template-columns: 1fr; gap: 1rem;">
  403. <div class="card">
  404. <div class="card-title"><span class="icon">🛡️</span> HIPAA Guard</div>
  405. <div class="card-desc">No external API leaks. Prompt parameters remain in internal RAM pools.</div>
  406. </div>
  407. <div class="card">
  408. <div class="card-title"><span class="icon">📈</span> High Efficiency</div>
  409. <div class="card-desc">Sub-second database responses via optimized views and B-Tree indexing.</div>
  410. </div>
  411. </div>
  412. </div>
  413. </div>
  414. <div class="slide-footer">
  415. <span>Local Food AI - Technical Presentation</span>
  416. <span>2 / 11</span>
  417. </div>
  418. </div>
  419. <!-- Slide 3: Multi-Container Architecture -->
  420. <div class="slide">
  421. <div class="slide-header">
  422. <span class="tag">Architecture Diagram</span>
  423. <span class="title-badge">LOCAL INFRASTRUCTURE</span>
  424. </div>
  425. <div class="slide-body">
  426. <h2 class="slide-title">Distributed Local Component Communication</h2>
  427. <div class="slide-grid-2" style="grid-template-columns: 1.3fr 0.7fr; gap: 1.5rem; align-items: center;">
  428. <svg class="architecture-svg" viewBox="0 0 500 250">
  429. <!-- Client -->
  430. <rect class="svg-box" x="10" y="90" width="100" height="40" style="fill:rgba(88, 166, 255, 0.1); stroke: var(--primary-accent);"/>
  431. <text class="svg-text" x="60" y="110">User Browser</text>
  432. <text class="svg-text-sub" x="60" y="122">HTTP Port 80</text>
  433. <!-- Proxy & App -->
  434. <rect class="svg-box" x="150" y="30" width="100" height="40"/>
  435. <text class="svg-text" x="200" y="50">Nginx Gateway</text>
  436. <text class="svg-text-sub" x="200" y="62">Port 80</text>
  437. <rect class="svg-box" x="150" y="150" width="100" height="40"/>
  438. <text class="svg-text" x="200" y="170">Streamlit UI</text>
  439. <text class="svg-text-sub" x="200" y="182">Port 8502</text>
  440. <!-- Backends -->
  441. <rect class="svg-box" x="350" y="15" width="110" height="40"/>
  442. <text class="svg-text" x="405" y="35">Ollama Daemon</text>
  443. <text class="svg-text-sub" x="405" y="47">Port 11434 (Llama3.2)</text>
  444. <rect class="svg-box" x="350" y="90" width="110" height="40"/>
  445. <text class="svg-text" x="405" y="110">MySQL Cluster</text>
  446. <text class="svg-text-sub" x="405" y="122">Port 3306 (Partitions)</text>
  447. <rect class="svg-box" x="350" y="165" width="110" height="40"/>
  448. <text class="svg-text" x="405" y="185">Zabbix Server</text>
  449. <text class="svg-text-sub" x="405" y="197">Port 10051 (SNMPv3)</text>
  450. <!-- Lines -->
  451. <path class="svg-line-active" d="M 110 110 L 150 50" />
  452. <path class="svg-line-active" d="M 200 70 L 200 150" />
  453. <path class="svg-line" d="M 250 160 L 350 35" />
  454. <path class="svg-line-active" d="M 250 170 L 350 110" />
  455. <path class="svg-line" d="M 250 180 L 350 185" />
  456. </svg>
  457. <div>
  458. <ul>
  459. <li><strong>Nginx Proxy</strong> routes all browser traffic to Streamlit.</li>
  460. <li><strong>Streamlit</strong> issues optimized view queries directly to <strong>MySQL</strong>.</li>
  461. <li>RAG flows communicate locally with <strong>Ollama</strong>.</li>
  462. <li><strong>Zabbix</strong> monitors all health points locally via secure SNMPv3 channels.</li>
  463. </ul>
  464. </div>
  465. </div>
  466. </div>
  467. <div class="slide-footer">
  468. <span>Local Food AI - Technical Presentation</span>
  469. <span>3 / 11</span>
  470. </div>
  471. </div>
  472. <!-- Slide 4: Database Design -->
  473. <div class="slide">
  474. <div class="slide-header">
  475. <span class="tag">Database Performance</span>
  476. <span class="title-badge">VERTICAL PARTITIONING</span>
  477. </div>
  478. <div class="slide-body">
  479. <h2 class="slide-title">Database Design: Grouped Partitioning</h2>
  480. <div class="slide-grid-2">
  481. <div>
  482. <ul>
  483. <li><strong>Vertically Partitioned Schema</strong>: To split the massive OpenFoodFacts attributes and avoid InnoDB row size limits, the data is partitioned across 5 tables:
  484. <span class="highlight-text">core, allergens, macros, vitamins, minerals</span>.
  485. </li>
  486. <li><strong>Unified SQL View</strong>: A single joined view presents records to the Streamlit app seamlessly.</li>
  487. <li><strong>FULLTEXT Search Indexes</strong>: Added to the `products_core` title column to bypass standard slow string matches.</li>
  488. </ul>
  489. </div>
  490. <div class="card">
  491. <div style="font-family: var(--font-mono); font-size: 1.3rem; margin-bottom: 1rem; color: var(--primary-accent); text-align: center; border-bottom: 1px dashed var(--border-color); padding-bottom: 0.5rem;">
  492. Performance Comparison
  493. </div>
  494. <div style="display: flex; justify-content: space-around; align-items: center; margin-top: 1.5rem;">
  495. <div style="text-align: center;">
  496. <div style="color: var(--text-secondary); font-size: 0.8rem; text-transform: uppercase;">Standard JOIN</div>
  497. <div style="font-size: 1.8rem; color: #ff7b72; font-weight: 700;">&gt; 4.20s</div>
  498. </div>
  499. <div style="font-size: 1.5rem; color: var(--text-secondary);">→</div>
  500. <div style="text-align: center;">
  501. <div style="color: var(--primary-accent); font-size: 0.8rem; text-transform: uppercase;">Optimized Index</div>
  502. <div style="font-size: 2.2rem; color: #56ff56; font-weight: 700;">&lt; 0.04s</div>
  503. </div>
  504. </div>
  505. <div style="font-size: 0.8rem; text-align: center; color: var(--text-secondary); margin-top: 1rem;">
  506. Sub-second response times achieved via strategic index bindings!
  507. </div>
  508. </div>
  509. </div>
  510. </div>
  511. <div class="slide-footer">
  512. <span>Local Food AI - Technical Presentation</span>
  513. <span>4 / 11</span>
  514. </div>
  515. </div>
  516. <!-- Slide 5: LLM and RAG -->
  517. <div class="slide">
  518. <div class="slide-header">
  519. <span class="tag">Artificial Intelligence</span>
  520. <span class="title-badge">LOCAL LLM / RAG</span>
  521. </div>
  522. <div class="slide-body">
  523. <h2 class="slide-title">Local LLM Choice & RAG Tool-Calling</h2>
  524. <div class="slide-grid-2">
  525. <div>
  526. <ul>
  527. <li><strong>Quantized `llama3.2:3b` Model</strong>: Runs completely locally under Ollama daemon. Consumes only 2.2 GB of RAM, fitting within the 16 GB hardware constraints.</li>
  528. <li><strong>Chain-of-Thought (CoT)</strong>: System prompts enforce XML-wrapped thinking tags (`<thought>...</thought>`) to explain dietary constraints.</li>
  529. <li><strong>Autonomous Tool Calling</strong>: Enables the LLM to trigger two local tools:
  530. <br>1. `search_nutrition_db` (direct SQL macro retrieval)
  531. <br>2. `local_web_search` (covert SearXNG search proxy)
  532. </li>
  533. </ul>
  534. </div>
  535. <pre><code># Ollama Tool Binding Example in app.py
  536. response = ollama.chat(
  537. model='llama3.2:3b',
  538. messages=st.session_state.messages,
  539. tools=[search_nutrition_db, local_web_search]
  540. )
  541. # Returns structured arguments to
  542. # query vertical database views!</code></pre>
  543. </div>
  544. </div>
  545. <div class="slide-footer">
  546. <span>Local Food AI - Technical Presentation</span>
  547. <span>5 / 11</span>
  548. </div>
  549. </div>
  550. <!-- Slide 6: Antigravity Subagents -->
  551. <div class="slide">
  552. <div class="slide-header">
  553. <span class="tag">Development Operations</span>
  554. <span class="title-badge">ANTIGRAVITY AGENTS</span>
  555. </div>
  556. <div class="slide-body">
  557. <h2 class="slide-title">AI Agent Orchestration: Antigravity Subagents</h2>
  558. <div class="slide-grid-3">
  559. <div class="card">
  560. <div class="card-title"><span class="icon">🎓</span> Expert Coach</div>
  561. <div class="card-desc">Reviewed vertical partition schemas, verified dynamic formats, and checked code complexity indices.</div>
  562. </div>
  563. <div class="card">
  564. <div class="card-title"><span class="icon">📝</span> Doc Writer</div>
  565. <div class="card-desc">Maintained operational guides and manuals in `/docs`, syncing them with codebase modifications.</div>
  566. </div>
  567. <div class="card">
  568. <div class="card-title"><span class="icon">🖇️</span> Git Commit</div>
  569. <div class="card-desc">Linked local commits to Gogs and triggered automatic updates to the Taiga task board.</div>
  570. </div>
  571. </div>
  572. <div class="slide-grid-2" style="margin-top: 1rem; grid-template-columns: 1fr 1fr;">
  573. <div class="card">
  574. <div class="card-title"><span class="icon">🛠️</span> SQL Optimizer</div>
  575. <div class="card-desc">Created database indices, vertical division schemas, and optimized JOIN layouts on unified views.</div>
  576. </div>
  577. <div class="card">
  578. <div class="card-title"><span class="icon">🔍</span> Code Reviewer</div>
  579. <div class="card-desc">Analyzed python scripts (`app.py`, `ingest_csv.py`) to resolve encoding issues and runtime errors.</div>
  580. </div>
  581. </div>
  582. </div>
  583. <div class="slide-footer">
  584. <span>Local Food AI - Technical Presentation</span>
  585. <span>6 / 11</span>
  586. </div>
  587. </div>
  588. <!-- Slide 7: Agent Permissions -->
  589. <div class="slide">
  590. <div class="slide-header">
  591. <span class="tag">Security Governance</span>
  592. <span class="title-badge">POLP BOUNDARIES</span>
  593. </div>
  594. <div class="slide-body">
  595. <h2 class="slide-title">Agent Permissions & Security Boundaries</h2>
  596. <div class="slide-grid-2">
  597. <div>
  598. <ul>
  599. <li><strong>Principle of Least Privilege (PoLP)</strong>: Configured tight boundaries to ensure the AI agent could not access sensitive host directory structures or execute unchecked network calls.</li>
  600. <li><strong>Path Lock constraints</strong>: Restricted file read and write operations exclusively to the local workspace folder.</li>
  601. <li><strong>Shell commands limits</strong>: Prevented shell command executes. Limited commands exclusively to `git`, `python`, `chmod`, `docker-compose`, and `Get-Content`.</li>
  602. </ul>
  603. </div>
  604. <div class="card" style="background: rgba(255, 123, 114, 0.05); border-color: rgba(255, 123, 114, 0.2);">
  605. <div class="card-title" style="color: #ff7b72;"><span class="icon">⚠️</span> Network Guardrails</div>
  606. <div class="card-desc" style="color: var(--text-secondary); margin-bottom: 0.5rem;">
  607. Network reads and executions were restricted solely to:
  608. </div>
  609. <div style="font-family: var(--font-mono); font-size: 0.85rem; color: #fff;">
  610. - 192.168.130.170 (Docker host)
  611. <br>- 192.168.130.161 (Taiga API)
  612. </div>
  613. <div style="margin-top: 0.8rem; font-size: 0.8rem; color: var(--text-secondary);">
  614. Zero external DNS queries or telemetry leaks allowed!
  615. </div>
  616. </div>
  617. </div>
  618. </div>
  619. <div class="slide-footer">
  620. <span>Local Food AI - Technical Presentation</span>
  621. <span>7 / 11</span>
  622. </div>
  623. </div>
  624. <!-- Slide 8: Technical Struggles -->
  625. <div class="slide">
  626. <div class="slide-header">
  627. <span class="tag">Lessons Learned</span>
  628. <span class="title-badge">REFLECTIONS</span>
  629. </div>
  630. <div class="slide-body">
  631. <h2 class="slide-title">Reflections: Engineering Struggles & Solutions</h2>
  632. <div class="slide-grid-3">
  633. <div class="card">
  634. <div class="card-title" style="font-size:1rem; color:#ff7b72;">1. Regex Greediness</div>
  635. <div class="card-desc" style="font-size:0.85rem;">
  636. <strong>Struggle:</strong> The smudge regex was too greedy and corrupted python literals across lines.
  637. <br><strong>Fix:</strong> Line-restricted the pattern (`[^\r\n$]+\$`) and split literals in the script.
  638. </div>
  639. </div>
  640. <div class="card">
  641. <div class="card-title" style="font-size:1rem; color:#ff7b72;">2. Filter Self-Delete</div>
  642. <div class="card-desc" style="font-size:0.85rem;">
  643. <strong>Struggle:</strong> Git checkout deleted the smudge script first, causing subsequent smudges to crash.
  644. <br><strong>Fix:</strong> Sequential checkout checking out the filter script before other files.
  645. </div>
  646. </div>
  647. <div class="card">
  648. <div class="card-title" style="font-size:1rem; color:#ff7b72;">3. Accent Encoding</div>
  649. <div class="card-desc" style="font-size:0.85rem;">
  650. <strong>Struggle:</strong> French accents in git metadata logs broke code writers under Windows codepages.
  651. <br><strong>Fix:</strong> Sanitizers processing python strings with `errors='replace'` writing to UTF-8.
  652. </div>
  653. </div>
  654. </div>
  655. </div>
  656. <div class="slide-footer">
  657. <span>Local Food AI - Technical Presentation</span>
  658. <span>8 / 11</span>
  659. </div>
  660. </div>
  661. <!-- Slide 9: Observability -->
  662. <div class="slide">
  663. <div class="slide-header">
  664. <span class="tag">DevOps Telemetry</span>
  665. <span class="title-badge">ZABBIX & SNMPv3</span>
  666. </div>
  667. <div class="slide-body">
  668. <h2 class="slide-title">Enterprise Observability & Real-Time Alerts</h2>
  669. <div class="slide-grid-2">
  670. <div>
  671. <ul>
  672. <li><strong>Secure SNMPv3 Architecture</strong>: Set up encrypted monitoring with username `securityUser` utilizing SHA verification and AES encryption keys.</li>
  673. <li><strong>Dynamic Telemetry Daemon</strong>: `zabbix_telemetry.py` polls system status indices and forwards database states.</li>
  674. <li><strong>Application Traps (`snmp_notifier.py`)</strong>: Directly triggers SNMP traps on major events:
  675. <br>- Failed credential attempts
  676. <br>- Password reset actions
  677. <br>- Database index delays
  678. </li>
  679. </ul>
  680. </div>
  681. <div class="card">
  682. <div class="card-title"><span class="icon">💬</span> Active Webhook Routing</div>
  683. <div class="card-desc" style="margin-bottom: 0.8rem;">
  684. Zabbix alerts parse the trap and automatically forward notification packets to Teams and Discord webhooks:
  685. </div>
  686. <pre style="max-height: 120px;"><code>{
  687. "event": "SNMPv3 Trap Alert",
  688. "node": "WSL-Workstation",
  689. "status": "PROBLEM",
  690. "desc": "High CPU utilization detected"
  691. }</code></pre>
  692. </div>
  693. </div>
  694. </div>
  695. <div class="slide-footer">
  696. <span>Local Food AI - Technical Presentation</span>
  697. <span>9 / 11</span>
  698. </div>
  699. </div>
  700. <!-- Slide 10: Privacy Verification -->
  701. <div class="slide">
  702. <div class="slide-header">
  703. <span class="tag">Security Audit</span>
  704. <span class="title-badge">DATA PRIVACY VERIFIED</span>
  705. </div>
  706. <div class="slide-body">
  707. <h2 class="slide-title">Data Privacy Audit: 100% Air-Gapped</h2>
  708. <div class="slide-grid-2">
  709. <div>
  710. <ul>
  711. <li><strong>Access Logs Verification</strong>: Audited Nginx reverse proxy configurations to confirm all user requests originate inside the local subnet range.</li>
  712. <li><strong>Container Network Separation</strong>: Database has no public port mappings. Runs in an isolated Docker bridge container network.</li>
  713. <li><strong>SearXNG Scraper Proxy</strong>: Strip tracking tags and user cookies prior to querying external domains.</li>
  714. </ul>
  715. </div>
  716. <div class="card" style="background: rgba(86, 255, 86, 0.03); border-color: rgba(86, 255, 86, 0.2);">
  717. <div class="card-title" style="color: #56ff56;"><span class="icon">🔒</span> TCPDump Network Sniffing Audit</div>
  718. <div class="card-desc" style="color: var(--text-primary); margin-bottom: 0.5rem;">
  719. Ran packet sniffing audits during intensive chat inference RAG queries:
  720. </div>
  721. <pre style="max-height: 90px; background: #000; border-color: #333;"><code style="color: #56ff56;">$ tcpdump -i eth0 dst port not 80 \
  722. and dst port not 22 and dst port not 161
  723. # Result: ZERO packets sent outbound to external IPs!</code></pre>
  724. <div style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 0.5rem;">
  725. Audited verification confirms no data escapes the host server!
  726. </div>
  727. </div>
  728. </div>
  729. </div>
  730. <div class="slide-footer">
  731. <span>Local Food AI - Technical Presentation</span>
  732. <span>10 / 11</span>
  733. </div>
  734. </div>
  735. <!-- Slide 11: Day 2 Operations -->
  736. <div class="slide">
  737. <div class="slide-header">
  738. <span class="tag">Production Maintenance</span>
  739. <span class="title-badge">FUTURE PLANNING</span>
  740. </div>
  741. <div class="slide-body">
  742. <h2 class="slide-title">Future Work & Day 2 Operations</h2>
  743. <div class="slide-grid-2">
  744. <div class="card">
  745. <div class="card-title"><span class="icon">🔑</span> SSL/TLS Encryption</div>
  746. <div class="card-desc">Upgrade the Nginx configuration to support HTTPS (Port 443) using the organization's root security authority to encrypt local workstation networks.</div>
  747. </div>
  748. <div class="card">
  749. <div class="card-title"><span class="icon">🧭</span> Database Migrations</div>
  750. <div class="card-desc">Rely strictly on Alembic scripts to roll out subsequent tables, preventing manual schema modifications from breaking view joins.</div>
  751. </div>
  752. <div class="card" style="margin-top: 1rem;">
  753. <div class="card-title"><span class="icon">⏱️</span> Rate Limiting</div>
  754. <div class="card-desc">Implement a sliding-window throttling algorithm in `app.py` to prevent potential clinician local server resource exhaustion.</div>
  755. </div>
  756. <div class="card" style="margin-top: 1rem;">
  757. <div class="card-title"><span class="icon">🧪</span> UAT Iteration Loops</div>
  758. <div class="card-desc">Incorporate clinical dietitian feedback to adjust nutritional threshold recommendations and warning tags.</div>
  759. </div>
  760. </div>
  761. </div>
  762. <div class="slide-footer">
  763. <span>Local Food AI - Technical Presentation</span>
  764. <span>11 / 11</span>
  765. </div>
  766. </div>
  767. </div>
  768. </div>
  769. <!-- Navigation Controls -->
  770. <div class="controls">
  771. <button class="nav-btn" id="prevBtn" aria-label="Previous slide">←</button>
  772. <div class="progress-bar-container">
  773. <div class="progress-bar" id="progress"></div>
  774. </div>
  775. <span class="slide-counter" id="counter">Slide 1 of 11</span>
  776. <button class="nav-btn" id="nextBtn" aria-label="Next slide">→</button>
  777. </div>
  778. <script>
  779. let currentSlide = 0;
  780. const slides = document.querySelectorAll('.slide');
  781. const totalSlides = slides.length;
  782. const prevBtn = document.getElementById('prevBtn');
  783. const nextBtn = document.getElementById('nextBtn');
  784. const progress = document.getElementById('progress');
  785. const counter = document.getElementById('counter');
  786. function updateSlides() {
  787. slides.forEach((slide, idx) => {
  788. slide.classList.remove('active', 'prev', 'next');
  789. if (idx === currentSlide) {
  790. slide.classList.add('active');
  791. } else if (idx < currentSlide) {
  792. slide.classList.add('prev');
  793. } else {
  794. slide.classList.add('next');
  795. }
  796. });
  797. // Update progress bar
  798. const pct = ((currentSlide + 1) / totalSlides) * 100;
  799. progress.style.width = `${pct}%`;
  800. // Update slide counter
  801. counter.innerText = `Slide ${currentSlide + 1} of ${totalSlides}`;
  802. // Disable buttons at bounds
  803. prevBtn.disabled = currentSlide === 0;
  804. nextBtn.disabled = currentSlide === totalSlides - 1;
  805. }
  806. prevBtn.addEventListener('click', () => {
  807. if (currentSlide > 0) {
  808. currentSlide--;
  809. updateSlides();
  810. }
  811. });
  812. nextBtn.addEventListener('click', () => {
  813. if (currentSlide < totalSlides - 1) {
  814. currentSlide++;
  815. updateSlides();
  816. }
  817. });
  818. // Key bindings
  819. document.addEventListener('keydown', (e) => {
  820. if (e.key === 'ArrowRight' || e.key === ' ') {
  821. if (currentSlide < totalSlides - 1) {
  822. currentSlide++;
  823. updateSlides();
  824. }
  825. } else if (e.key === 'ArrowLeft') {
  826. if (currentSlide > 0) {
  827. currentSlide--;
  828. updateSlides();
  829. }
  830. }
  831. });
  832. updateSlides();
  833. </script>
  834. </body>
  835. </html>