ARCHITECTURE.html 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>auto_put_ad_mini · 系统架构图谱</title>
  7. <link rel="preconnect" href="https://fonts.googleapis.com">
  8. <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  9. <link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght,SOFT@9..144,300..900,0..100&family=IBM+Plex+Sans:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500;700&family=Noto+Serif+SC:wght@400;500;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
  10. <style>
  11. :root {
  12. --bg: #0E141B;
  13. --bg-alt: #141C26;
  14. --panel: #1A2330;
  15. --panel-hi: #212C3D;
  16. --border: #2A3849;
  17. --border-hi: #3D5066;
  18. --text: #E8E1D3;
  19. --text-dim: #B8B0A1;
  20. --text-muted: #7A8294;
  21. --grid: #1F2A38;
  22. --tools: #E0A458;
  23. --tools-soft: #E0A45822;
  24. --skills: #A4B494;
  25. --skills-soft: #A4B49422;
  26. --decisions: #5BC0BE;
  27. --decisions-soft: #5BC0BE22;
  28. --rules: #F25F5C;
  29. --rules-soft: #F25F5C22;
  30. --data: #6B9FB5;
  31. --data-soft: #6B9FB522;
  32. --gold: #D4AF37;
  33. --serif: "Fraunces", "Noto Serif SC", Georgia, serif;
  34. --sans: "IBM Plex Sans", "Noto Sans SC", system-ui, sans-serif;
  35. --mono: "JetBrains Mono", "SF Mono", Consolas, monospace;
  36. }
  37. * { box-sizing: border-box; margin: 0; padding: 0; }
  38. html { scroll-behavior: smooth; }
  39. body {
  40. background: var(--bg);
  41. color: var(--text);
  42. font-family: var(--sans);
  43. font-weight: 400;
  44. line-height: 1.6;
  45. letter-spacing: 0.005em;
  46. min-height: 100vh;
  47. overflow-x: hidden;
  48. background-image:
  49. radial-gradient(at 20% 0%, rgba(91, 192, 190, 0.08) 0px, transparent 40%),
  50. radial-gradient(at 80% 100%, rgba(224, 164, 88, 0.06) 0px, transparent 50%),
  51. linear-gradient(0deg, var(--bg) 0%, #0A0F15 100%);
  52. }
  53. /* Subtle grain overlay */
  54. body::before {
  55. content: "";
  56. position: fixed;
  57. inset: 0;
  58. pointer-events: none;
  59. z-index: 1;
  60. opacity: 0.025;
  61. background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' /%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  62. }
  63. main { position: relative; z-index: 2; }
  64. /* ====== HEADER ====== */
  65. .hero {
  66. padding: 80px 60px 100px;
  67. border-bottom: 1px solid var(--border);
  68. position: relative;
  69. overflow: hidden;
  70. }
  71. .hero::before {
  72. content: "";
  73. position: absolute;
  74. inset: 0;
  75. background:
  76. repeating-linear-gradient(0deg, transparent 0, transparent 49px, var(--grid) 49px, var(--grid) 50px),
  77. repeating-linear-gradient(90deg, transparent 0, transparent 49px, var(--grid) 49px, var(--grid) 50px);
  78. opacity: 0.4;
  79. pointer-events: none;
  80. mask-image: radial-gradient(ellipse at 30% 50%, black 30%, transparent 80%);
  81. }
  82. .hero-inner { position: relative; max-width: 1400px; margin: 0 auto; }
  83. .eyebrow {
  84. font-family: var(--mono);
  85. font-size: 11px;
  86. text-transform: uppercase;
  87. letter-spacing: 0.25em;
  88. color: var(--tools);
  89. margin-bottom: 28px;
  90. display: flex;
  91. align-items: center;
  92. gap: 12px;
  93. }
  94. .eyebrow::before {
  95. content: "";
  96. display: block;
  97. width: 32px;
  98. height: 1px;
  99. background: var(--tools);
  100. }
  101. h1.title {
  102. font-family: var(--serif);
  103. font-weight: 300;
  104. font-size: clamp(48px, 8vw, 112px);
  105. line-height: 0.95;
  106. letter-spacing: -0.03em;
  107. color: var(--text);
  108. margin-bottom: 24px;
  109. font-feature-settings: "ss01", "ss02";
  110. font-variation-settings: "opsz" 144, "SOFT" 30;
  111. }
  112. h1.title em {
  113. font-style: italic;
  114. color: var(--tools);
  115. font-variation-settings: "opsz" 144, "SOFT" 100;
  116. }
  117. .tagline {
  118. font-family: var(--serif);
  119. font-size: 24px;
  120. font-weight: 300;
  121. font-style: italic;
  122. color: var(--text-dim);
  123. max-width: 720px;
  124. margin-bottom: 48px;
  125. line-height: 1.4;
  126. font-variation-settings: "opsz" 24;
  127. }
  128. .meta-grid {
  129. display: grid;
  130. grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  131. gap: 32px 48px;
  132. padding-top: 32px;
  133. border-top: 1px solid var(--border);
  134. max-width: 1100px;
  135. }
  136. .meta-item {
  137. display: flex;
  138. flex-direction: column;
  139. gap: 6px;
  140. }
  141. .meta-label {
  142. font-family: var(--mono);
  143. font-size: 10px;
  144. text-transform: uppercase;
  145. letter-spacing: 0.18em;
  146. color: var(--text-muted);
  147. }
  148. .meta-value {
  149. font-family: var(--mono);
  150. font-size: 14px;
  151. color: var(--text);
  152. font-weight: 500;
  153. }
  154. .meta-value.accent { color: var(--tools); }
  155. /* ====== SECTIONS ====== */
  156. section {
  157. padding: 100px 60px;
  158. max-width: 1400px;
  159. margin: 0 auto;
  160. border-bottom: 1px solid var(--border);
  161. }
  162. .section-head {
  163. display: flex;
  164. align-items: baseline;
  165. justify-content: space-between;
  166. margin-bottom: 60px;
  167. gap: 40px;
  168. flex-wrap: wrap;
  169. }
  170. .section-num {
  171. font-family: var(--mono);
  172. font-size: 13px;
  173. color: var(--text-muted);
  174. letter-spacing: 0.2em;
  175. }
  176. .section-title {
  177. font-family: var(--serif);
  178. font-weight: 300;
  179. font-size: clamp(36px, 5vw, 64px);
  180. line-height: 1.05;
  181. letter-spacing: -0.02em;
  182. flex: 1;
  183. font-variation-settings: "opsz" 96, "SOFT" 40;
  184. }
  185. .section-title em {
  186. font-style: italic;
  187. font-variation-settings: "opsz" 96, "SOFT" 100;
  188. }
  189. .section-title.tools em { color: var(--tools); }
  190. .section-title.skills em { color: var(--skills); }
  191. .section-title.decisions em { color: var(--decisions); }
  192. .section-title.rules em { color: var(--rules); }
  193. .section-title.data em { color: var(--data); }
  194. .section-desc {
  195. font-family: var(--serif);
  196. font-style: italic;
  197. font-size: 16px;
  198. color: var(--text-dim);
  199. max-width: 480px;
  200. font-weight: 300;
  201. line-height: 1.5;
  202. }
  203. /* ====== ARCHITECTURE LAYERS ====== */
  204. .layer-stack {
  205. display: grid;
  206. gap: 24px;
  207. }
  208. .layer {
  209. display: grid;
  210. grid-template-columns: 180px 1fr;
  211. gap: 32px;
  212. padding: 32px 36px;
  213. background: var(--panel);
  214. border: 1px solid var(--border);
  215. border-left-width: 3px;
  216. position: relative;
  217. transition: all 0.3s ease;
  218. }
  219. .layer:hover {
  220. background: var(--panel-hi);
  221. border-left-width: 6px;
  222. }
  223. .layer-1 { border-left-color: var(--tools); }
  224. .layer-2 { border-left-color: var(--skills); }
  225. .layer-3 { border-left-color: var(--decisions); }
  226. .layer-4 { border-left-color: var(--rules); }
  227. .layer-id {
  228. font-family: var(--mono);
  229. font-size: 12px;
  230. letter-spacing: 0.15em;
  231. color: var(--text-muted);
  232. text-transform: uppercase;
  233. margin-bottom: 8px;
  234. }
  235. .layer-name {
  236. font-family: var(--serif);
  237. font-size: 28px;
  238. font-weight: 400;
  239. line-height: 1.1;
  240. letter-spacing: -0.01em;
  241. font-variation-settings: "opsz" 32;
  242. }
  243. .layer-content { font-size: 14px; line-height: 1.7; }
  244. .layer-content code {
  245. font-family: var(--mono);
  246. font-size: 12.5px;
  247. color: var(--tools);
  248. background: var(--bg-alt);
  249. padding: 2px 6px;
  250. border-radius: 2px;
  251. }
  252. .layer-content ul {
  253. list-style: none;
  254. display: grid;
  255. grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  256. gap: 12px;
  257. margin-top: 8px;
  258. }
  259. .layer-content ul li {
  260. padding: 10px 14px;
  261. background: var(--bg-alt);
  262. border-left: 2px solid var(--border-hi);
  263. font-size: 13px;
  264. color: var(--text-dim);
  265. }
  266. .layer-content ul li strong {
  267. color: var(--text);
  268. font-weight: 500;
  269. }
  270. /* ====== PIPELINE ====== */
  271. .pipeline {
  272. display: grid;
  273. gap: 0;
  274. position: relative;
  275. }
  276. .pipeline-step {
  277. display: grid;
  278. grid-template-columns: 80px 1fr 280px;
  279. gap: 32px;
  280. padding: 32px 0;
  281. border-top: 1px solid var(--border);
  282. position: relative;
  283. transition: background 0.25s ease;
  284. }
  285. .pipeline-step:hover { background: rgba(91, 192, 190, 0.03); }
  286. .pipeline-step:last-child { border-bottom: 1px solid var(--border); }
  287. .step-num {
  288. font-family: var(--serif);
  289. font-style: italic;
  290. font-size: 88px;
  291. line-height: 0.9;
  292. font-weight: 300;
  293. color: var(--decisions);
  294. letter-spacing: -0.04em;
  295. font-variation-settings: "opsz" 144, "SOFT" 80;
  296. opacity: 0.85;
  297. }
  298. .step-body { padding-top: 16px; }
  299. .step-name {
  300. font-family: var(--serif);
  301. font-size: 26px;
  302. font-weight: 400;
  303. margin-bottom: 8px;
  304. letter-spacing: -0.01em;
  305. font-variation-settings: "opsz" 32;
  306. }
  307. .step-tool {
  308. display: inline-block;
  309. font-family: var(--mono);
  310. font-size: 11.5px;
  311. color: var(--tools);
  312. background: var(--tools-soft);
  313. padding: 3px 10px;
  314. margin-bottom: 14px;
  315. border: 1px solid var(--tools);
  316. border-radius: 2px;
  317. }
  318. .step-desc {
  319. font-size: 14.5px;
  320. color: var(--text-dim);
  321. line-height: 1.65;
  322. max-width: 600px;
  323. }
  324. .step-io {
  325. padding-top: 18px;
  326. font-family: var(--mono);
  327. font-size: 11.5px;
  328. color: var(--text-muted);
  329. display: grid;
  330. gap: 6px;
  331. }
  332. .step-io span {
  333. display: flex;
  334. gap: 8px;
  335. }
  336. .step-io span::before {
  337. content: "→";
  338. color: var(--data);
  339. font-weight: bold;
  340. }
  341. .step-io.input span::before { content: "↓"; color: var(--data); }
  342. .step-io.output span::before { content: "↑"; color: var(--decisions); }
  343. /* ====== SKILLS GRID ====== */
  344. .skills-grid {
  345. display: grid;
  346. grid-template-columns: repeat(2, 1fr);
  347. gap: 4px;
  348. background: var(--border);
  349. border: 1px solid var(--border);
  350. }
  351. .skill-card {
  352. background: var(--panel);
  353. padding: 36px 36px 32px;
  354. position: relative;
  355. transition: all 0.3s ease;
  356. cursor: default;
  357. }
  358. .skill-card:hover {
  359. background: var(--panel-hi);
  360. transform: translateZ(0);
  361. }
  362. .skill-card.featured {
  363. grid-column: span 2;
  364. background: linear-gradient(135deg, var(--panel) 0%, #1F2A36 100%);
  365. }
  366. .skill-rank {
  367. position: absolute;
  368. top: 24px;
  369. right: 28px;
  370. font-family: var(--mono);
  371. font-size: 11px;
  372. letter-spacing: 0.15em;
  373. color: var(--text-muted);
  374. text-transform: uppercase;
  375. }
  376. .skill-rank.core {
  377. color: var(--gold);
  378. }
  379. .skill-rank.core::before {
  380. content: "◆ ";
  381. color: var(--gold);
  382. }
  383. .skill-name {
  384. font-family: var(--mono);
  385. font-size: 13px;
  386. letter-spacing: 0.1em;
  387. color: var(--skills);
  388. margin-bottom: 14px;
  389. }
  390. .skill-headline {
  391. font-family: var(--serif);
  392. font-size: 28px;
  393. font-weight: 400;
  394. line-height: 1.15;
  395. letter-spacing: -0.01em;
  396. margin-bottom: 16px;
  397. font-variation-settings: "opsz" 32, "SOFT" 30;
  398. }
  399. .skill-card.featured .skill-headline {
  400. font-size: 36px;
  401. font-style: italic;
  402. font-variation-settings: "opsz" 48, "SOFT" 80;
  403. }
  404. .skill-meta {
  405. font-family: var(--mono);
  406. font-size: 11px;
  407. color: var(--text-muted);
  408. margin-bottom: 18px;
  409. letter-spacing: 0.05em;
  410. }
  411. .skill-desc {
  412. font-size: 14px;
  413. color: var(--text-dim);
  414. line-height: 1.7;
  415. margin-bottom: 20px;
  416. }
  417. .skill-sections {
  418. display: flex;
  419. flex-wrap: wrap;
  420. gap: 6px;
  421. }
  422. .skill-tag {
  423. font-family: var(--mono);
  424. font-size: 10.5px;
  425. padding: 4px 10px;
  426. background: var(--bg-alt);
  427. color: var(--text-dim);
  428. border: 1px solid var(--border);
  429. letter-spacing: 0.03em;
  430. }
  431. .skill-card.featured .skill-tag {
  432. border-color: var(--decisions);
  433. color: var(--decisions);
  434. }
  435. /* ====== TOOLS NETWORK ====== */
  436. .tools-table {
  437. display: grid;
  438. grid-template-columns: 1fr;
  439. gap: 0;
  440. border: 1px solid var(--border);
  441. background: var(--panel);
  442. }
  443. .tools-row {
  444. display: grid;
  445. grid-template-columns: 240px 200px 1fr 140px;
  446. gap: 24px;
  447. padding: 18px 28px;
  448. border-bottom: 1px solid var(--border);
  449. align-items: center;
  450. transition: background 0.2s;
  451. }
  452. .tools-row:last-child { border-bottom: none; }
  453. .tools-row:hover { background: var(--panel-hi); }
  454. .tools-row.head {
  455. background: var(--bg-alt);
  456. border-bottom: 2px solid var(--border-hi);
  457. }
  458. .tools-row.head > div {
  459. font-family: var(--mono);
  460. font-size: 10.5px;
  461. text-transform: uppercase;
  462. letter-spacing: 0.18em;
  463. color: var(--text-muted);
  464. }
  465. .tool-file {
  466. font-family: var(--mono);
  467. font-size: 13px;
  468. color: var(--tools);
  469. }
  470. .tool-cat {
  471. font-family: var(--mono);
  472. font-size: 11.5px;
  473. letter-spacing: 0.05em;
  474. text-transform: uppercase;
  475. }
  476. .tool-cat.data { color: var(--data); }
  477. .tool-cat.compute { color: var(--decisions); }
  478. .tool-cat.decide { color: var(--skills); }
  479. .tool-cat.execute { color: var(--rules); }
  480. .tool-cat.report { color: var(--gold); }
  481. .tool-fn {
  482. font-size: 13px;
  483. color: var(--text-dim);
  484. line-height: 1.6;
  485. }
  486. .tool-fn code {
  487. font-family: var(--mono);
  488. font-size: 12px;
  489. color: var(--text);
  490. background: var(--bg);
  491. padding: 1px 6px;
  492. border-radius: 2px;
  493. }
  494. .tool-step {
  495. font-family: var(--mono);
  496. font-size: 11px;
  497. color: var(--text-muted);
  498. text-align: right;
  499. }
  500. /* ====== DECISION FRAMEWORK ====== */
  501. .decision-arch {
  502. display: grid;
  503. grid-template-columns: 1fr 1fr;
  504. gap: 60px;
  505. align-items: start;
  506. }
  507. .decision-tower {
  508. display: grid;
  509. gap: 12px;
  510. }
  511. .tier {
  512. position: relative;
  513. padding: 28px 32px;
  514. background: var(--panel);
  515. border: 1px solid var(--border);
  516. }
  517. .tier::before {
  518. content: "";
  519. position: absolute;
  520. left: 0;
  521. top: 0;
  522. bottom: 0;
  523. width: 4px;
  524. }
  525. .tier-1::before { background: var(--rules); }
  526. .tier-2::before { background: var(--data); }
  527. .tier-3::before { background: var(--decisions); }
  528. .tier-4::before { background: var(--skills); }
  529. .tier-label {
  530. font-family: var(--mono);
  531. font-size: 11px;
  532. letter-spacing: 0.2em;
  533. margin-bottom: 6px;
  534. text-transform: uppercase;
  535. }
  536. .tier-1 .tier-label { color: var(--rules); }
  537. .tier-2 .tier-label { color: var(--data); }
  538. .tier-3 .tier-label { color: var(--decisions); }
  539. .tier-4 .tier-label { color: var(--skills); }
  540. .tier-name {
  541. font-family: var(--serif);
  542. font-size: 22px;
  543. font-weight: 400;
  544. margin-bottom: 8px;
  545. font-variation-settings: "opsz" 24;
  546. }
  547. .tier-desc {
  548. font-size: 13.5px;
  549. color: var(--text-dim);
  550. line-height: 1.65;
  551. }
  552. .tier-desc code {
  553. font-family: var(--mono);
  554. font-size: 12px;
  555. color: var(--text);
  556. background: var(--bg-alt);
  557. padding: 1px 5px;
  558. border-radius: 2px;
  559. }
  560. /* Actions panel */
  561. .actions-panel {
  562. background: var(--panel);
  563. border: 1px solid var(--border);
  564. padding: 32px;
  565. }
  566. .actions-head {
  567. font-family: var(--serif);
  568. font-size: 22px;
  569. font-weight: 400;
  570. margin-bottom: 24px;
  571. font-variation-settings: "opsz" 24;
  572. }
  573. .actions-list { display: grid; gap: 14px; }
  574. .action-row {
  575. display: grid;
  576. grid-template-columns: 110px 1fr;
  577. gap: 16px;
  578. padding: 12px 0;
  579. border-bottom: 1px solid var(--border);
  580. }
  581. .action-row:last-child { border-bottom: none; }
  582. .action-name {
  583. font-family: var(--mono);
  584. font-size: 13px;
  585. letter-spacing: 0.05em;
  586. display: flex;
  587. align-items: baseline;
  588. gap: 8px;
  589. }
  590. .action-name .dot {
  591. display: inline-block;
  592. width: 8px;
  593. height: 8px;
  594. border-radius: 50%;
  595. }
  596. .action-name.pause .dot { background: var(--rules); }
  597. .action-name.bid_down .dot { background: #FFA94D; }
  598. .action-name.bid_up .dot { background: var(--decisions); }
  599. .action-name.scale_up .dot { background: var(--skills); }
  600. .action-name.creative_adjust .dot { background: var(--gold); }
  601. .action-name.observe .dot { background: var(--data); }
  602. .action-name.hold .dot { background: var(--text-muted); }
  603. .action-name code {
  604. color: var(--text);
  605. }
  606. .action-rule {
  607. font-size: 13px;
  608. color: var(--text-dim);
  609. line-height: 1.55;
  610. }
  611. /* ====== THRESHOLDS ====== */
  612. .thresholds-grid {
  613. display: grid;
  614. grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  615. gap: 4px;
  616. background: var(--border);
  617. border: 1px solid var(--border);
  618. }
  619. .threshold-card {
  620. background: var(--panel);
  621. padding: 28px;
  622. position: relative;
  623. transition: background 0.25s;
  624. }
  625. .threshold-card:hover { background: var(--panel-hi); }
  626. .threshold-key {
  627. font-family: var(--mono);
  628. font-size: 11.5px;
  629. color: var(--text-muted);
  630. letter-spacing: 0.08em;
  631. margin-bottom: 14px;
  632. }
  633. .threshold-value {
  634. font-family: var(--serif);
  635. font-size: 56px;
  636. font-weight: 300;
  637. line-height: 1;
  638. font-style: italic;
  639. letter-spacing: -0.03em;
  640. font-variation-settings: "opsz" 96, "SOFT" 100;
  641. margin-bottom: 6px;
  642. }
  643. .threshold-value.tools { color: var(--tools); }
  644. .threshold-value.decisions { color: var(--decisions); }
  645. .threshold-value.rules { color: var(--rules); }
  646. .threshold-value.skills { color: var(--skills); }
  647. .threshold-unit {
  648. font-family: var(--mono);
  649. font-size: 13px;
  650. color: var(--text-muted);
  651. margin-left: 6px;
  652. font-style: normal;
  653. font-weight: 400;
  654. }
  655. .threshold-meaning {
  656. font-size: 13px;
  657. color: var(--text-dim);
  658. line-height: 1.5;
  659. margin-top: 14px;
  660. padding-top: 14px;
  661. border-top: 1px solid var(--border);
  662. }
  663. /* ====== DATA FLOW ====== */
  664. .flow-svg {
  665. width: 100%;
  666. height: auto;
  667. background: var(--panel);
  668. border: 1px solid var(--border);
  669. padding: 20px;
  670. }
  671. .flow-svg text {
  672. font-family: var(--mono);
  673. font-size: 11px;
  674. fill: var(--text-dim);
  675. }
  676. .flow-svg .node {
  677. fill: var(--bg-alt);
  678. stroke: var(--border-hi);
  679. stroke-width: 1;
  680. }
  681. .flow-svg .node-data { stroke: var(--data); }
  682. .flow-svg .node-tool { stroke: var(--tools); }
  683. .flow-svg .node-decide { stroke: var(--decisions); }
  684. .flow-svg .node-act { stroke: var(--rules); }
  685. .flow-svg .label-bold {
  686. font-family: var(--serif);
  687. font-size: 13px;
  688. fill: var(--text);
  689. font-weight: 500;
  690. }
  691. .flow-svg .arrow {
  692. stroke: var(--border-hi);
  693. stroke-width: 1.5;
  694. fill: none;
  695. marker-end: url(#arrowhead);
  696. }
  697. .flow-svg .arrow-data { stroke: var(--data); }
  698. .flow-svg .arrow-decide { stroke: var(--decisions); }
  699. /* ====== DEPLOYMENT ====== */
  700. .deploy-grid {
  701. display: grid;
  702. grid-template-columns: 1fr 1fr;
  703. gap: 32px;
  704. }
  705. .deploy-card {
  706. padding: 32px;
  707. background: var(--panel);
  708. border: 1px solid var(--border);
  709. }
  710. .deploy-mode {
  711. font-family: var(--mono);
  712. font-size: 11px;
  713. letter-spacing: 0.2em;
  714. color: var(--gold);
  715. text-transform: uppercase;
  716. margin-bottom: 12px;
  717. }
  718. .deploy-name {
  719. font-family: var(--serif);
  720. font-size: 26px;
  721. font-weight: 400;
  722. margin-bottom: 16px;
  723. font-variation-settings: "opsz" 32;
  724. }
  725. .deploy-desc {
  726. font-size: 14px;
  727. color: var(--text-dim);
  728. line-height: 1.7;
  729. margin-bottom: 20px;
  730. }
  731. .deploy-files { display: grid; gap: 6px; }
  732. .deploy-files code {
  733. font-family: var(--mono);
  734. font-size: 12px;
  735. color: var(--text);
  736. display: flex;
  737. align-items: center;
  738. gap: 12px;
  739. padding: 8px 12px;
  740. background: var(--bg-alt);
  741. border-left: 2px solid var(--gold);
  742. }
  743. .deploy-files code span {
  744. color: var(--text-muted);
  745. font-size: 11px;
  746. }
  747. /* ====== FOOTER ====== */
  748. footer {
  749. padding: 60px;
  750. text-align: center;
  751. border-top: 1px solid var(--border);
  752. color: var(--text-muted);
  753. font-family: var(--mono);
  754. font-size: 12px;
  755. letter-spacing: 0.1em;
  756. position: relative;
  757. z-index: 2;
  758. }
  759. footer p { margin-bottom: 8px; }
  760. footer .stamp {
  761. display: inline-block;
  762. margin-top: 24px;
  763. padding: 8px 16px;
  764. border: 1px solid var(--border);
  765. font-family: var(--serif);
  766. font-style: italic;
  767. font-size: 13px;
  768. letter-spacing: 0.03em;
  769. color: var(--text-dim);
  770. }
  771. /* ====== ANIMATIONS ====== */
  772. @keyframes fadeUp {
  773. from { opacity: 0; transform: translateY(20px); }
  774. to { opacity: 1; transform: translateY(0); }
  775. }
  776. .hero-inner > * {
  777. animation: fadeUp 0.8s ease-out backwards;
  778. }
  779. .hero-inner > *:nth-child(1) { animation-delay: 0.05s; }
  780. .hero-inner > *:nth-child(2) { animation-delay: 0.15s; }
  781. .hero-inner > *:nth-child(3) { animation-delay: 0.25s; }
  782. .hero-inner > *:nth-child(4) { animation-delay: 0.35s; }
  783. /* Responsive */
  784. @media (max-width: 900px) {
  785. section, .hero { padding-left: 28px; padding-right: 28px; }
  786. .layer { grid-template-columns: 1fr; }
  787. .pipeline-step { grid-template-columns: 60px 1fr; }
  788. .step-io { grid-column: 1 / -1; padding-left: 0; padding-top: 8px; }
  789. .skills-grid, .deploy-grid { grid-template-columns: 1fr; }
  790. .skill-card.featured { grid-column: span 1; }
  791. .decision-arch { grid-template-columns: 1fr; }
  792. .tools-row { grid-template-columns: 1fr; gap: 4px; }
  793. .tools-row > div { padding: 2px 0; }
  794. .tool-step { text-align: left; }
  795. }
  796. </style>
  797. </head>
  798. <body>
  799. <main>
  800. <!-- ====== HERO ====== -->
  801. <header class="hero">
  802. <div class="hero-inner">
  803. <div class="eyebrow">Reson Agent · 业务示例 · v0.4.27</div>
  804. <h1 class="title">auto_put_ad<em>_mini</em></h1>
  805. <p class="tagline">数据驱动的广告调控 Agent —— 把 ODPS 裂变数据、动态 ROI、规则候选与 LLM 综合判断,串成单向链路的 ROI 优化系统。</p>
  806. <div class="meta-grid">
  807. <div class="meta-item">
  808. <span class="meta-label">业务场景</span>
  809. <span class="meta-value">微信小程序投流</span>
  810. </div>
  811. <div class="meta-item">
  812. <span class="meta-label">承载类型</span>
  813. <span class="meta-value">MARKETING_CARRIER_TYPE_<br>MINI_PROGRAM_WECHAT</span>
  814. </div>
  815. <div class="meta-item">
  816. <span class="meta-label">出价模式</span>
  817. <span class="meta-value">BID_MODE_OCPM</span>
  818. </div>
  819. <div class="meta-item">
  820. <span class="meta-label">LLM 后端</span>
  821. <span class="meta-value accent">OpenRouter · Claude Sonnet 4.5</span>
  822. </div>
  823. <div class="meta-item">
  824. <span class="meta-label">数据窗口</span>
  825. <span class="meta-value">14 天 / 7 日均值</span>
  826. </div>
  827. <div class="meta-item">
  828. <span class="meta-label">执行开关</span>
  829. <span class="meta-value">EXECUTION_ENABLED = False</span>
  830. </div>
  831. <div class="meta-item">
  832. <span class="meta-label">入口</span>
  833. <span class="meta-value">run.py · execute_once.py</span>
  834. </div>
  835. <div class="meta-item">
  836. <span class="meta-label">部署</span>
  837. <span class="meta-value">K8s CronJob · 02:00 UTC</span>
  838. </div>
  839. </div>
  840. </div>
  841. </header>
  842. <!-- ====== 01 · ARCHITECTURE LAYERS ====== -->
  843. <section id="architecture">
  844. <div class="section-head">
  845. <div>
  846. <div class="section-num">§ 01 — 整体架构</div>
  847. <h2 class="section-title tools">框架与业务的<em>双层解耦</em></h2>
  848. </div>
  849. <p class="section-desc">框架(<code>agent/</code>)只关心 LLM 调度 + 工具调度 + 持久化,完全不感知业务。业务层通过 <code>@tool</code> 装饰器和 <code>.md</code> skill 单向注入,从不反向依赖。</p>
  850. </div>
  851. <div class="layer-stack">
  852. <div class="layer layer-1">
  853. <div>
  854. <div class="layer-id">L1 · Framework</div>
  855. <div class="layer-name">Reson Agent</div>
  856. </div>
  857. <div class="layer-content">
  858. <p>通用 Agent 框架,只读,业务零侵入。</p>
  859. <ul>
  860. <li><strong>AgentRunner</strong> · ReAct 主循环 · new/resume/rewind</li>
  861. <li><strong>ToolRegistry</strong> · @tool 装饰器 + JSON Schema</li>
  862. <li><strong>SkillLoader</strong> · 扫 .md + frontmatter 注入 prompt</li>
  863. <li><strong>FileSystemTraceStore</strong> · 持久化 .trace/ + 侧分支</li>
  864. <li><strong>LLM 适配层</strong> · OpenRouter / Qwen / Gemini</li>
  865. </ul>
  866. </div>
  867. </div>
  868. <div class="layer layer-2">
  869. <div>
  870. <div class="layer-id">L2 · Skills</div>
  871. <div class="layer-name">领域知识</div>
  872. </div>
  873. <div class="layer-content">
  874. <p>4 份 markdown,启动时注入 system prompt,定义业务术语 + 决策原则 + 平台硬约束。</p>
  875. <ul>
  876. <li><strong>ad_domain</strong> · R 值人群 + 裂变变现模型</li>
  877. <li><strong>platform_rules</strong> · 腾讯平台硬约束(优先级最高)</li>
  878. <li><strong>decision_strategy</strong> · 7 action 决策框架(核心 19.5K)</li>
  879. <li><strong>posterior_wisdom</strong> · 后验经验查询入口</li>
  880. </ul>
  881. </div>
  882. </div>
  883. <div class="layer layer-3">
  884. <div>
  885. <div class="layer-id">L3 · Tools</div>
  886. <div class="layer-name">业务工具</div>
  887. </div>
  888. <div class="layer-content">
  889. <p>10+ 个工具组成 10 步流水线,从数据采集到决策执行单向流转。</p>
  890. <ul>
  891. <li><strong>data_query</strong> · ODPS 拉数 + 多日合并</li>
  892. <li><strong>roi_calculator</strong> · 创意聚合 + 动态 ROI</li>
  893. <li><strong>portfolio_metrics</strong> · 渠道 P50 + tier 基线</li>
  894. <li><strong>ad_decision</strong> · 候选标记 + 决策合并</li>
  895. <li><strong>guardrails</strong> · 护栏校验</li>
  896. <li><strong>execution_engine</strong> · 分级执行 + 审计</li>
  897. <li><strong>im_approval</strong> · 飞书审批 + 阻塞 polling</li>
  898. <li><strong>report_generator</strong> · Excel 决策表</li>
  899. <li><strong>feishu_doc</strong> · 飞书文档 + IM 推送</li>
  900. <li><strong>ad_api</strong> · 腾讯广告 API 封装</li>
  901. </ul>
  902. </div>
  903. </div>
  904. <div class="layer layer-4">
  905. <div>
  906. <div class="layer-id">L4 · Config</div>
  907. <div class="layer-name">硬约束与开关</div>
  908. </div>
  909. <div class="layer-content">
  910. <p>所有阈值、白名单、执行开关集中于 <code>config.py</code>。运行时可被数据库覆盖。</p>
  911. <ul>
  912. <li><strong>ROI 阈值</strong> · ROI_LOW_FACTOR = 0.75</li>
  913. <li><strong>调价边界</strong> · BID_FLOOR/CEILING = 0.05/1.00 元</li>
  914. <li><strong>年龄保护</strong> · COLD_START_DAYS = 3, EARLY_GROWTH_DAYS = 7</li>
  915. <li><strong>频率限制</strong> · MAX_ADJUSTMENTS_PER_AD_PER_DAY = 2</li>
  916. <li><strong>执行开关</strong> · EXECUTION_ENABLED = False</li>
  917. </ul>
  918. </div>
  919. </div>
  920. </div>
  921. </section>
  922. <!-- ====== 02 · PIPELINE ====== -->
  923. <section id="pipeline">
  924. <div class="section-head">
  925. <div>
  926. <div class="section-num">§ 02 — 数据流水线</div>
  927. <h2 class="section-title decisions">十步,从 ODPS <em>到飞书</em></h2>
  928. </div>
  929. <p class="section-desc">单向链路:数据采集 → 指标计算 → 候选筛选 → LLM 推理 → 决策合并 → 护栏 → 审批 → 执行 → 报告。每步前置依赖严格。</p>
  930. </div>
  931. <div class="pipeline">
  932. <div class="pipeline-step">
  933. <div class="step-num">01</div>
  934. <div class="step-body">
  935. <div class="step-name">fetch_creative_data</div>
  936. <span class="step-tool">tools/data_query.py</span>
  937. <p class="step-desc">从 ODPS 表 <code>loghubods.touliu_data</code> 增量拉取 14 天创意级原始数据(消耗、首层打开数、T0 裂变数、总回流人数、总收入)。已存在的日期文件自动跳过。</p>
  938. </div>
  939. <div class="step-io">
  940. <span class="output">outputs/raw/creative_{date}.csv</span>
  941. <span class="output">outputs/ad_status/ad_status_{date}.csv</span>
  942. </div>
  943. </div>
  944. <div class="pipeline-step">
  945. <div class="step-num">02</div>
  946. <div class="step-body">
  947. <div class="step-name">merge_creative_data</div>
  948. <span class="step-tool">tools/data_query.py</span>
  949. <p class="step-desc">每日 raw + 广告状态做 left join,前置过滤 DELETED/SUSPEND 状态。生成单日 merged CSV。</p>
  950. </div>
  951. <div class="step-io">
  952. <span class="output">outputs/merged/merged_{date}.csv</span>
  953. </div>
  954. </div>
  955. <div class="pipeline-step">
  956. <div class="step-num">03</div>
  957. <div class="step-body">
  958. <div class="step-name">calculate_roi_metrics</div>
  959. <span class="step-tool">tools/roi_calculator.py</span>
  960. <p class="step-desc">加载 30 天 merged · 创意 → 广告聚合 · 计算 <code>动态ROI = 当日裂变收益率 × 裂变效率稳定因子</code> · 7 日滚动均值(min_periods=3)· 日消耗 &lt; 100 元的天数标 NaN 不入均值。<strong>含临时 TEMP 块过滤近 7 天累计消耗 = 0 的广告</strong>。</p>
  961. </div>
  962. <div class="step-io">
  963. <span class="output">outputs/metrics_{date}.csv (广告级)</span>
  964. </div>
  965. </div>
  966. <div class="pipeline-step">
  967. <div class="step-num">04</div>
  968. <div class="step-body">
  969. <div class="step-name">portfolio_metrics</div>
  970. <span class="step-tool">tools/portfolio_metrics.py</span>
  971. <p class="step-desc">在 calculate_roi_metrics 内自动调用。计算渠道 P25/P50/P75 ROI 基线 + 人群包(tier)级 fission/CTR/bid 均值,作为决策对比基准。</p>
  972. </div>
  973. <div class="step-io">
  974. <span class="output">outputs/portfolio_summary/portfolio_summary_{date}.json</span>
  975. </div>
  976. </div>
  977. <div class="pipeline-step">
  978. <div class="step-num">05</div>
  979. <div class="step-body">
  980. <div class="step-name">get_ads_for_review</div>
  981. <span class="step-tool">tools/ad_decision.py</span>
  982. <p class="step-desc">三层分类:零消耗待关停 / 候选广告 / 正常运行。计算 5 个候选标记:<code>roi_low</code>、<code>decay_signal</code>、<code>bid_up_candidate</code>(双分支)、<code>bid_down_candidate</code>、<code>scale_up_candidate</code>。年龄保护:≤3 天剔除,4-7 天屏蔽负向标记。按 audience_tier 分批返回。</p>
  983. </div>
  984. <div class="step-io">
  985. <span class="output">JSON: tier_batches[ ]</span>
  986. </div>
  987. </div>
  988. <div class="pipeline-step">
  989. <div class="step-num">06</div>
  990. <div class="step-body">
  991. <div class="step-name">LLM 推理(分批)</div>
  992. <span class="step-tool">主 Agent</span>
  993. <p class="step-desc">按 tier 分批调用,降低单次输入 60-80%(避免 lost in the middle)。每批 LLM 输出决策 JSON 数组(<code>action</code>/<code>dimension</code>/<code>reason</code>/<code>confidence</code>/<code>recommended_change_pct</code>)。<strong>禁止多次调 apply_decisions</strong>(后调吞前调)。</p>
  994. </div>
  995. <div class="step-io">
  996. <span class="output">JSON 累积数组(全部 tier)</span>
  997. </div>
  998. </div>
  999. <div class="pipeline-step">
  1000. <div class="step-num">07</div>
  1001. <div class="step-body">
  1002. <div class="step-name">apply_decisions</div>
  1003. <span class="step-tool">tools/ad_decision.py</span>
  1004. <p class="step-desc">合并三类决策来源:零消耗自动关停(规则) + LLM 输出(主) + 正常运行兜底 hold(规则)。<strong>左连接 metrics CSV 补全</strong>(ad_name / ad_age_days / cost_7d_avg / 动态ROI / audience_tier 等 20+ 字段)。</p>
  1005. </div>
  1006. <div class="step-io">
  1007. <span class="output">outputs/reports/llm_decisions_{date}.csv</span>
  1008. </div>
  1009. </div>
  1010. <div class="pipeline-step">
  1011. <div class="step-num">08</div>
  1012. <div class="step-body">
  1013. <div class="step-name">validate_decisions</div>
  1014. <span class="step-tool">tools/guardrails.py</span>
  1015. <p class="step-desc">护栏校验:冷启动保护、出价上下界(0.05-1.00 元)、单日调整频率(≤2 次)、单次累计变化(≤20%)、数据新鲜度(≤96h)。失败的决策会被强制改 hold + 记录原因。</p>
  1016. </div>
  1017. <div class="step-io">
  1018. <span class="output">outputs/reports/validated_decisions_{date}.csv</span>
  1019. </div>
  1020. </div>
  1021. <div class="pipeline-step">
  1022. <div class="step-num">09</div>
  1023. <div class="step-body">
  1024. <div class="step-name">send_approval_request</div>
  1025. <span class="step-tool">tools/im_approval.py · feishu_doc.py</span>
  1026. <p class="step-desc">前置过滤 <code>FEISHU_EXCLUDE_ACTIONS = {hold, observe, scale_up, bid_up}</code>(消息和表格用同一份过滤后数据,数字一致)。生成审批 xlsx 上传飞书文档,带预览链接的卡片推送给操作员私聊 + 项目群,阻塞轮询 chat_history 等回复(超时 120 分钟)。</p>
  1027. </div>
  1028. <div class="step-io">
  1029. <span class="output">飞书 sheets URL · approvals/{request_id}/</span>
  1030. </div>
  1031. </div>
  1032. <div class="pipeline-step">
  1033. <div class="step-num">10</div>
  1034. <div class="step-body">
  1035. <div class="step-name">execute_decisions + send_feishu_text_message</div>
  1036. <span class="step-tool">tools/execution_engine.py · ad_api.py</span>
  1037. <p class="step-desc">分级执行(tier 1 自动 / tier 2-3 审批通过)。<code>EXECUTION_ENABLED=False</code> 时仅 log 不调腾讯 API。完成后发送简单文字摘要("已执行 N 条:关停 X / 降价 Y"),不再发完整报告。</p>
  1038. </div>
  1039. <div class="step-io">
  1040. <span class="output">outputs/execution_log/{date}.csv</span>
  1041. <span class="output">飞书文字消息</span>
  1042. </div>
  1043. </div>
  1044. </div>
  1045. </section>
  1046. <!-- ====== 03 · SKILLS ====== -->
  1047. <section id="skills">
  1048. <div class="section-head">
  1049. <div>
  1050. <div class="section-num">§ 03 — 领域知识(Skills)</div>
  1051. <h2 class="section-title skills">注入 prompt 的<em>四份手册</em></h2>
  1052. </div>
  1053. <p class="section-desc">.md + YAML frontmatter,框架启动时按 <code>name</code> 索引,内容拼接到 system prompt 末尾。冲突优先级:platform-rules &gt; decision-strategy &gt; posterior-wisdom &gt; ad-domain。</p>
  1054. </div>
  1055. <div class="skills-grid">
  1056. <div class="skill-card featured">
  1057. <span class="skill-rank core">CORE</span>
  1058. <div class="skill-name">decision_strategy.md</div>
  1059. <h3 class="skill-headline">决策的判断框架与 reason 输出规范</h3>
  1060. <div class="skill-meta">19.5 KB · 约 570 行 · 决策 80% 重量</div>
  1061. <p class="skill-desc">真正的"作战手册"。三层架构(规则候选 → LLM → 输出规范),三维对比基准(ROI 看渠道P50,裂变看同类,CTR 看同类),7 种 action 详解 × 触发前提 × 综合权衡 × pct 要求 × 禁用场景。reason 5 元组模板 + 提交前自检 5 问。</p>
  1062. <div class="skill-sections">
  1063. <span class="skill-tag">§一 · 角色与原则</span>
  1064. <span class="skill-tag">§二 · 候选标记解读</span>
  1065. <span class="skill-tag">§三 · 年龄策略</span>
  1066. <span class="skill-tag">§四 · 决策思考 5 步</span>
  1067. <span class="skill-tag">§五 · 7 action 详解</span>
  1068. <span class="skill-tag">§六 · 多因素权衡 + 冲突表</span>
  1069. <span class="skill-tag">§七 · 输出规范 + 自检</span>
  1070. </div>
  1071. </div>
  1072. <div class="skill-card">
  1073. <span class="skill-rank">DOMAIN</span>
  1074. <div class="skill-name">ad_domain.md</div>
  1075. <h3 class="skill-headline">微信小程序投流的业务模型</h3>
  1076. <div class="skill-meta">6.5 KB · 业务知识基底</div>
  1077. <p class="skill-desc">解释裂变变现模型("每一层都在变现",非线性漏斗)、R 值人群含义(R500/R330+/R330/R180/R100/R50/R10/R2 的带人能力 + 投放单价 + 当日 ROI 表现)、动态 ROI 公式与字段定义。</p>
  1078. <div class="skill-sections">
  1079. <span class="skill-tag">变现模型</span>
  1080. <span class="skill-tag">R 值对照表</span>
  1081. <span class="skill-tag">动态 ROI 公式</span>
  1082. <span class="skill-tag">字段定义</span>
  1083. </div>
  1084. </div>
  1085. <div class="skill-card">
  1086. <span class="skill-rank">PLATFORM</span>
  1087. <div class="skill-name">platform_rules.md</div>
  1088. <h3 class="skill-headline">腾讯广告平台硬约束</h3>
  1089. <div class="skill-meta">5.3 KB · 优先级最高</div>
  1090. <p class="skill-desc">oCPM 学习期(前 5 转化 / 24h 内 ≤2 次调整)、调价幅度上限(单次 ≤30%)、少广告多素材策略(单广告创意数 ≥5)、素材疲劳识别、数据口径(T+1 而非实时)、API QPS 与批量限制。</p>
  1091. <div class="skill-sections">
  1092. <span class="skill-tag">学习期保护</span>
  1093. <span class="skill-tag">调价上限</span>
  1094. <span class="skill-tag">数据口径</span>
  1095. <span class="skill-tag">API 约束</span>
  1096. </div>
  1097. </div>
  1098. <div class="skill-card">
  1099. <span class="skill-rank">POSTERIOR</span>
  1100. <div class="skill-name">posterior_wisdom.md</div>
  1101. <h3 class="skill-headline">后验经验查询入口</h3>
  1102. <div class="skill-meta">0.8 KB · 待积累</div>
  1103. <p class="skill-desc">通过 <code>ask_knowledge()</code> 查询历史投放案例。当前系统尚未积累足够后验数据,提供查询模板:周期性效应、调价效果、人群包对比、异常模式、创意冷启动等。</p>
  1104. <div class="skill-sections">
  1105. <span class="skill-tag">ask_knowledge</span>
  1106. <span class="skill-tag">查询模板</span>
  1107. </div>
  1108. </div>
  1109. </div>
  1110. </section>
  1111. <!-- ====== 04 · TOOLS ====== -->
  1112. <section id="tools">
  1113. <div class="section-head">
  1114. <div>
  1115. <div class="section-num">§ 04 — 工具清单</div>
  1116. <h2 class="section-title tools">十二个 <em>tool</em> · 按数据流向排序</h2>
  1117. </div>
  1118. <p class="section-desc">每个工具用 <code>@tool</code> 装饰注册到 ToolRegistry,JSON Schema 自动生成。隐藏参数(context、trace_id)由 Runner 注入,LLM 看不到。</p>
  1119. </div>
  1120. <div class="tools-table">
  1121. <div class="tools-row head">
  1122. <div>文件 / 函数</div>
  1123. <div>分类</div>
  1124. <div>职责</div>
  1125. <div>所属步骤</div>
  1126. </div>
  1127. <div class="tools-row">
  1128. <div class="tool-file">data_query.py</div>
  1129. <div class="tool-cat data">DATA</div>
  1130. <div class="tool-fn"><code>fetch_creative_data</code> + <code>merge_creative_data</code> · 从 ODPS 拉创意级原始数据,合并多日 + 广告状态。</div>
  1131. <div class="tool-step">Step 01—02</div>
  1132. </div>
  1133. <div class="tools-row">
  1134. <div class="tool-file">roi_calculator.py</div>
  1135. <div class="tool-cat compute">COMPUTE</div>
  1136. <div class="tool-fn"><code>calculate_roi_metrics</code> · 创意 → 广告聚合,计算动态 ROI(7 日均值 + 裂变效率稳定因子)。</div>
  1137. <div class="tool-step">Step 03</div>
  1138. </div>
  1139. <div class="tools-row">
  1140. <div class="tool-file">portfolio_metrics.py</div>
  1141. <div class="tool-cat compute">COMPUTE</div>
  1142. <div class="tool-fn"><code>_describe_group</code> + <code>_compute_daily_tier_snapshot</code> + <code>_compute_market_signal</code> · 渠道 P25/P50/P75 + tier 均值,作为决策基线。</div>
  1143. <div class="tool-step">Step 04</div>
  1144. </div>
  1145. <div class="tools-row">
  1146. <div class="tool-file">ad_decision.py</div>
  1147. <div class="tool-cat decide">DECIDE</div>
  1148. <div class="tool-fn"><code>get_ads_for_review</code> + <code>apply_decisions</code> + <code>query_ad_detail</code> + <code>modify_decisions</code> · 候选标记 + 三层分类 + LLM 决策合并。</div>
  1149. <div class="tool-step">Step 05, 07</div>
  1150. </div>
  1151. <div class="tools-row">
  1152. <div class="tool-file">guardrails.py</div>
  1153. <div class="tool-cat decide">VALIDATE</div>
  1154. <div class="tool-fn"><code>validate_decisions</code> · 冷启动 / 出价边界 / 频率 / 数据新鲜度 多重护栏校验,失败强制改 hold。</div>
  1155. <div class="tool-step">Step 08</div>
  1156. </div>
  1157. <div class="tools-row">
  1158. <div class="tool-file">execution_engine.py</div>
  1159. <div class="tool-cat execute">EXECUTE</div>
  1160. <div class="tool-fn"><code>execute_decisions</code> + <code>_classify_tier</code> · 分级执行,EXECUTION_ENABLED 控制是否真改腾讯 API。</div>
  1161. <div class="tool-step">Step 10</div>
  1162. </div>
  1163. <div class="tools-row">
  1164. <div class="tool-file">im_approval.py</div>
  1165. <div class="tool-cat execute">EXECUTE</div>
  1166. <div class="tool-fn"><code>send_approval_request</code> + <code>send_feishu_text_message</code> · 飞书审批阻塞轮询,FEISHU_EXCLUDE_ACTIONS 前置过滤。</div>
  1167. <div class="tool-step">Step 09—10</div>
  1168. </div>
  1169. <div class="tools-row">
  1170. <div class="tool-file">report_generator.py</div>
  1171. <div class="tool-cat report">REPORT</div>
  1172. <div class="tool-fn"><code>generate_report</code> · 带条件格式的 Excel 决策表(action 颜色编码)。</div>
  1173. <div class="tool-step">Step 10</div>
  1174. </div>
  1175. <div class="tools-row">
  1176. <div class="tool-file">feishu_doc.py</div>
  1177. <div class="tool-cat report">REPORT</div>
  1178. <div class="tool-fn"><code>import_to_feishu</code> · 上传 xlsx 到飞书文档,设置权限,带预览链接的 IM 卡片推送。</div>
  1179. <div class="tool-step">Step 09</div>
  1180. </div>
  1181. <div class="tools-row">
  1182. <div class="tool-file">ad_api.py</div>
  1183. <div class="tool-cat execute">EXECUTE</div>
  1184. <div class="tool-fn">腾讯广告 Marketing API v3.0 封装(/v3.0/adgroups/get / update),指数退避 + 操作幂等键。</div>
  1185. <div class="tool-step">Step 10</div>
  1186. </div>
  1187. <div class="tools-row">
  1188. <div class="tool-file">creative_metrics.py</div>
  1189. <div class="tool-cat compute">COMPUTE</div>
  1190. <div class="tool-fn">创意级数据指标聚合,服务于 ROI 计算上游。</div>
  1191. <div class="tool-step">Step 03 (内部)</div>
  1192. </div>
  1193. <div class="tools-row">
  1194. <div class="tool-file">odps_module.py</div>
  1195. <div class="tool-cat data">DATA</div>
  1196. <div class="tool-fn">ODPS 客户端封装,SQL 模板执行 + tunnel session 下载。</div>
  1197. <div class="tool-step">Step 01 (内部)</div>
  1198. </div>
  1199. </div>
  1200. </section>
  1201. <!-- ====== 05 · DECISION FRAMEWORK ====== -->
  1202. <section id="decision">
  1203. <div class="section-head">
  1204. <div>
  1205. <div class="section-num">§ 05 — 决策框架</div>
  1206. <h2 class="section-title decisions">规则与 LLM 的<em>四层协作</em></h2>
  1207. </div>
  1208. <p class="section-desc">规则定"是否评估",LLM 定"如何操作"。年龄保护是硬约束,候选标记决定进入 LLM 的范围,LLM 在已限定的边界内做综合判断,兜底层覆盖未决策广告。</p>
  1209. </div>
  1210. <div class="decision-arch">
  1211. <div class="decision-tower">
  1212. <div class="tier tier-1">
  1213. <div class="tier-label">L1 · Hard Gate</div>
  1214. <div class="tier-name">年龄保护(硬约束)</div>
  1215. <p class="tier-desc">≤<code>COLD_START_DAYS</code>(3 天):完全剔除,不评估。<br>4-<code>EARLY_GROWTH_DAYS</code>(7 天):屏蔽 <code>roi_low</code> / <code>decay_signal</code> / <code>bid_down_candidate</code>,只放过提价/扩量信号。</p>
  1216. </div>
  1217. <div class="tier tier-2">
  1218. <div class="tier-label">L2 · Rule</div>
  1219. <div class="tier-name">候选标记(5 个 flag)</div>
  1220. <p class="tier-desc">规则层在 <code>get_ads_for_review</code> 中输出 <code>roi_low</code> / <code>decay_signal</code> / <code>bid_up_candidate</code>(双分支:唤醒沉默 + 优质放量) / <code>bid_down_candidate</code> / <code>scale_up_candidate</code>。决定哪些广告进入 LLM。</p>
  1221. </div>
  1222. <div class="tier tier-3">
  1223. <div class="tier-label">L3 · LLM</div>
  1224. <div class="tier-name">综合判断(7 action)</div>
  1225. <p class="tier-desc">LLM 看候选标记 + 同类基线 + 调价历史 + tier 组合,从 7 种 action 中选一个 + 写 reason(5 元组规范)。可"越权":如规则没标但有强烈信号。</p>
  1226. </div>
  1227. <div class="tier tier-4">
  1228. <div class="tier-label">L4 · Fallback</div>
  1229. <div class="tier-name">兜底检查(规则)</div>
  1230. <p class="tier-desc"><code>apply_decisions</code> 末尾,所有未被 LLM 覆盖的广告自动归 <code>hold</code>(<code>source=规则判断</code>)。确保所有候选都有决策落盘,无遗漏。</p>
  1231. </div>
  1232. </div>
  1233. <div class="actions-panel">
  1234. <div class="actions-head">7 种 Action 触发条件</div>
  1235. <div class="actions-list">
  1236. <div class="action-row">
  1237. <div class="action-name pause"><span class="dot"></span><code>pause</code></div>
  1238. <div class="action-rule"><code>roi_low=True</code> + LLM 综合判断(双低/调价无效);或 7 日均消耗&lt;10 自动关停。pct = 0。</div>
  1239. </div>
  1240. <div class="action-row">
  1241. <div class="action-name bid_down"><span class="dot"></span><code>bid_down</code></div>
  1242. <div class="action-rule"><code>bid_down_candidate=True</code> + 裂变低于同类 + 成熟期 + 7 日均消耗 ≥500。pct ∈ [-5%, -3%]。</div>
  1243. </div>
  1244. <div class="action-row">
  1245. <div class="action-name bid_up"><span class="dot"></span><code>bid_up</code></div>
  1246. <div class="action-rule"><code>bid_up_candidate=True</code>(仅 4-7 天)。分支 A 唤醒沉默(低消耗 + CTR 正常)/ 分支 B 优质放量(高 ROI + 高裂变)。pct ∈ [+5%, +10%]。</div>
  1247. </div>
  1248. <div class="action-row">
  1249. <div class="action-name scale_up"><span class="dot"></span><code>scale_up</code></div>
  1250. <div class="action-rule"><code>scale_up_candidate=True</code>(成熟 &gt;7 天 + 稳定 ≥7 天 + 高消耗 &gt;1000 + ROI ≥ P50×0.9)。pct = 0(由运营新增广告/创意)。</div>
  1251. </div>
  1252. <div class="action-row">
  1253. <div class="action-name creative_adjust"><span class="dot"></span><code>creative_adjust</code></div>
  1254. <div class="action-rule">ROI 达标但消耗起不来 / CTR + CVR 同跌(素材疲劳)/ 已换创意但裂变仍低。pct = 0,人工换素材。</div>
  1255. </div>
  1256. <div class="action-row">
  1257. <div class="action-name observe"><span class="dot"></span><code>observe</code></div>
  1258. <div class="action-rule">数据不充分(roi_valid_days &lt;5)/ 7 天内已调价(等效果显现)/ 信号冲突(ROI 低但裂变好)。pct = 0。</div>
  1259. </div>
  1260. <div class="action-row">
  1261. <div class="action-name hold"><span class="dot"></span><code>hold</code></div>
  1262. <div class="action-rule">无异常信号(默认兜底)。pct = 0。</div>
  1263. </div>
  1264. </div>
  1265. </div>
  1266. </div>
  1267. </section>
  1268. <!-- ====== 06 · THRESHOLDS ====== -->
  1269. <section id="thresholds">
  1270. <div class="section-head">
  1271. <div>
  1272. <div class="section-num">§ 06 — 关键阈值</div>
  1273. <h2 class="section-title rules">决策的<em>数值边界</em></h2>
  1274. </div>
  1275. <p class="section-desc">所有阈值集中在 <code>config.py</code>,部分可被数据库 <code>tencent_ad_autoput.config</code> 表覆盖。修改前应通过 evaluation 验证。</p>
  1276. </div>
  1277. <div class="thresholds-grid">
  1278. <div class="threshold-card">
  1279. <div class="threshold-key">ROI_LOW_FACTOR</div>
  1280. <div class="threshold-value rules">0.75</div>
  1281. <p class="threshold-meaning">关停线乘子 · 动态ROI &lt; 渠道P50×0.75 → 候选关停</p>
  1282. </div>
  1283. <div class="threshold-card">
  1284. <div class="threshold-key">BID_DOWN_ROI_FACTOR</div>
  1285. <div class="threshold-value tools">0.90</div>
  1286. <p class="threshold-meaning">降价线乘子 · ROI &lt; P50×0.90 + 裂变低 → 候选降价</p>
  1287. </div>
  1288. <div class="threshold-card">
  1289. <div class="threshold-key">BID_UP_ROI_FACTOR</div>
  1290. <div class="threshold-value decisions">1.05</div>
  1291. <p class="threshold-meaning">提价线乘子 · ROI &gt; P50×1.05 + 早期成长期 → 候选提价</p>
  1292. </div>
  1293. <div class="threshold-card">
  1294. <div class="threshold-key">COLD_START_DAYS</div>
  1295. <div class="threshold-value rules">3 <span class="threshold-unit">天</span></div>
  1296. <p class="threshold-meaning">冷启动期 · 完全保护,不评估,不调整</p>
  1297. </div>
  1298. <div class="threshold-card">
  1299. <div class="threshold-key">EARLY_GROWTH_DAYS</div>
  1300. <div class="threshold-value skills">7 <span class="threshold-unit">天</span></div>
  1301. <p class="threshold-meaning">早期成长期 · 仅允许 bid_up / scale_up</p>
  1302. </div>
  1303. <div class="threshold-card">
  1304. <div class="threshold-key">MIN_DAILY_COST</div>
  1305. <div class="threshold-value tools">100 <span class="threshold-unit">元</span></div>
  1306. <p class="threshold-meaning">日消耗 &lt; 100 元 → ROI 当日数据不入 7 日滚动均值</p>
  1307. </div>
  1308. <div class="threshold-card">
  1309. <div class="threshold-key">NO_SPEND_THRESHOLD</div>
  1310. <div class="threshold-value rules">10 <span class="threshold-unit">元</span></div>
  1311. <p class="threshold-meaning">7 日均消耗 &lt; 10 → 自动 pause(年龄&gt;7 天)</p>
  1312. </div>
  1313. <div class="threshold-card">
  1314. <div class="threshold-key">BID_DOWN range</div>
  1315. <div class="threshold-value tools">3 — 5<span class="threshold-unit">%</span></div>
  1316. <p class="threshold-meaning">降价幅度 · 单次绝对值上限 5%(更严守)</p>
  1317. </div>
  1318. <div class="threshold-card">
  1319. <div class="threshold-key">BID_UP range</div>
  1320. <div class="threshold-value decisions">5 — 10<span class="threshold-unit">%</span></div>
  1321. <p class="threshold-meaning">提价幅度 · 单次绝对值上限 10%(可放宽)</p>
  1322. </div>
  1323. <div class="threshold-card">
  1324. <div class="threshold-key">BID_FLOOR / CEILING</div>
  1325. <div class="threshold-value skills">0.05 — 1.00<span class="threshold-unit">元</span></div>
  1326. <p class="threshold-meaning">出价硬边界 · 任何调整不得突破</p>
  1327. </div>
  1328. <div class="threshold-card">
  1329. <div class="threshold-key">MAX_ADJUSTMENTS_PER_AD_PER_DAY</div>
  1330. <div class="threshold-value rules">2</div>
  1331. <p class="threshold-meaning">单广告每日最大调整次数 · 平台学习期防抖</p>
  1332. </div>
  1333. <div class="threshold-card">
  1334. <div class="threshold-key">EXECUTION_ENABLED</div>
  1335. <div class="threshold-value rules">false</div>
  1336. <p class="threshold-meaning">执行开关 · 默认关闭,所有操作走审批不真改</p>
  1337. </div>
  1338. </div>
  1339. </section>
  1340. <!-- ====== 07 · DATA FLOW ====== -->
  1341. <section id="dataflow">
  1342. <div class="section-head">
  1343. <div>
  1344. <div class="section-num">§ 07 — 数据流向</div>
  1345. <h2 class="section-title data">从 ODPS 到<em>腾讯广告 API</em></h2>
  1346. </div>
  1347. <p class="section-desc">外部数据源 → 本地落盘 → 内存计算 → LLM 调用 → 飞书人机交互 → 腾讯广告写入。所有中间产物均落盘,可独立回放。</p>
  1348. </div>
  1349. <svg class="flow-svg" viewBox="0 0 1200 580" xmlns="http://www.w3.org/2000/svg">
  1350. <defs>
  1351. <marker id="arrowhead" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
  1352. <path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor"/>
  1353. </marker>
  1354. <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
  1355. <path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1F2A38" stroke-width="0.5"/>
  1356. </pattern>
  1357. </defs>
  1358. <rect width="1200" height="580" fill="url(#grid)" opacity="0.5"/>
  1359. <!-- Layers labels -->
  1360. <text x="20" y="40" class="label-bold" fill="#94A3B0">EXTERNAL</text>
  1361. <text x="20" y="170" class="label-bold" fill="#94A3B0">FILESYSTEM</text>
  1362. <text x="20" y="320" class="label-bold" fill="#94A3B0">COMPUTE</text>
  1363. <text x="20" y="450" class="label-bold" fill="#94A3B0">HUMAN + ACT</text>
  1364. <!-- External data sources -->
  1365. <rect x="160" y="20" width="160" height="50" class="node node-data" rx="2"/>
  1366. <text x="240" y="42" text-anchor="middle" class="label-bold">ODPS</text>
  1367. <text x="240" y="58" text-anchor="middle">loghubods.touliu_data</text>
  1368. <rect x="380" y="20" width="160" height="50" class="node node-data" rx="2"/>
  1369. <text x="460" y="42" text-anchor="middle" class="label-bold">腾讯广告 API</text>
  1370. <text x="460" y="58" text-anchor="middle">/adgroups/get</text>
  1371. <rect x="600" y="20" width="160" height="50" class="node node-data" rx="2"/>
  1372. <text x="680" y="42" text-anchor="middle" class="label-bold">RDS MySQL</text>
  1373. <text x="680" y="58" text-anchor="middle">config + whitelist</text>
  1374. <rect x="820" y="20" width="160" height="50" class="node node-data" rx="2"/>
  1375. <text x="900" y="42" text-anchor="middle" class="label-bold">飞书 IM</text>
  1376. <text x="900" y="58" text-anchor="middle">app + chat_history</text>
  1377. <!-- Filesystem layer -->
  1378. <rect x="80" y="150" width="180" height="40" class="node" rx="2"/>
  1379. <text x="170" y="174" text-anchor="middle">outputs/raw/</text>
  1380. <rect x="280" y="150" width="180" height="40" class="node" rx="2"/>
  1381. <text x="370" y="174" text-anchor="middle">outputs/merged/</text>
  1382. <rect x="480" y="150" width="180" height="40" class="node" rx="2"/>
  1383. <text x="570" y="174" text-anchor="middle">outputs/metrics_*.csv</text>
  1384. <rect x="680" y="150" width="180" height="40" class="node" rx="2"/>
  1385. <text x="770" y="174" text-anchor="middle">portfolio_summary/</text>
  1386. <rect x="880" y="150" width="220" height="40" class="node" rx="2"/>
  1387. <text x="990" y="174" text-anchor="middle">outputs/reports/</text>
  1388. <!-- Compute layer -->
  1389. <rect x="80" y="290" width="220" height="50" class="node node-tool" rx="2"/>
  1390. <text x="190" y="312" text-anchor="middle" class="label-bold">data_query</text>
  1391. <text x="190" y="328" text-anchor="middle">fetch + merge</text>
  1392. <rect x="320" y="290" width="220" height="50" class="node node-tool" rx="2"/>
  1393. <text x="430" y="312" text-anchor="middle" class="label-bold">roi_calculator</text>
  1394. <text x="430" y="328" text-anchor="middle">动态 ROI 7 日均值</text>
  1395. <rect x="560" y="290" width="220" height="50" class="node node-decide" rx="2"/>
  1396. <text x="670" y="312" text-anchor="middle" class="label-bold">ad_decision</text>
  1397. <text x="670" y="328" text-anchor="middle">候选标记 + 决策合并</text>
  1398. <rect x="800" y="290" width="220" height="50" class="node node-decide" rx="2"/>
  1399. <text x="910" y="312" text-anchor="middle" class="label-bold">LLM (Sonnet 4.5)</text>
  1400. <text x="910" y="328" text-anchor="middle">action + reason</text>
  1401. <!-- Human + Act layer -->
  1402. <rect x="200" y="430" width="200" height="50" class="node node-act" rx="2"/>
  1403. <text x="300" y="452" text-anchor="middle" class="label-bold">guardrails</text>
  1404. <text x="300" y="468" text-anchor="middle">护栏校验</text>
  1405. <rect x="450" y="430" width="200" height="50" class="node node-act" rx="2"/>
  1406. <text x="550" y="452" text-anchor="middle" class="label-bold">im_approval</text>
  1407. <text x="550" y="468" text-anchor="middle">飞书审批 + 阻塞</text>
  1408. <rect x="700" y="430" width="200" height="50" class="node node-act" rx="2"/>
  1409. <text x="800" y="452" text-anchor="middle" class="label-bold">execution_engine</text>
  1410. <text x="800" y="468" text-anchor="middle">EXECUTION_ENABLED?</text>
  1411. <rect x="950" y="430" width="200" height="50" class="node node-act" rx="2"/>
  1412. <text x="1050" y="452" text-anchor="middle" class="label-bold">ad_api</text>
  1413. <text x="1050" y="468" text-anchor="middle">/adgroups/update</text>
  1414. <!-- Connections -->
  1415. <g style="color:#6B9FB5">
  1416. <path class="arrow arrow-data" d="M 240 70 L 240 145 L 170 145 L 170 150"/>
  1417. <path class="arrow arrow-data" d="M 460 70 L 460 145 L 370 145 L 370 150"/>
  1418. </g>
  1419. <g style="color:#3D5066">
  1420. <path class="arrow" d="M 170 190 L 170 285 L 190 290"/>
  1421. <path class="arrow" d="M 370 190 L 370 290 L 430 290"/>
  1422. <path class="arrow" d="M 570 190 L 570 285 L 670 290"/>
  1423. <path class="arrow" d="M 770 190 L 770 285 L 870 290"/>
  1424. </g>
  1425. <!-- Compute connections -->
  1426. <g style="color:#3D5066">
  1427. <path class="arrow" d="M 300 315 L 320 315"/>
  1428. <path class="arrow" d="M 540 315 L 560 315"/>
  1429. <path class="arrow" d="M 780 315 L 800 315"/>
  1430. </g>
  1431. <!-- Compute → Output -->
  1432. <g style="color:#3D5066">
  1433. <path class="arrow" d="M 190 290 L 190 195"/>
  1434. <path class="arrow" d="M 430 290 L 430 195"/>
  1435. <path class="arrow" d="M 670 290 L 670 195"/>
  1436. <path class="arrow" d="M 910 290 L 990 195"/>
  1437. </g>
  1438. <!-- LLM → guardrails / approval -->
  1439. <g style="color:#5BC0BE">
  1440. <path class="arrow arrow-decide" d="M 910 340 L 910 410 L 300 410 L 300 430"/>
  1441. <path class="arrow arrow-decide" d="M 300 480 L 300 510 L 550 510 L 550 480"/>
  1442. </g>
  1443. <!-- approval → execution -->
  1444. <g style="color:#3D5066">
  1445. <path class="arrow" d="M 650 455 L 700 455"/>
  1446. <path class="arrow" d="M 900 455 L 950 455"/>
  1447. </g>
  1448. <!-- Feishu connection -->
  1449. <g style="color:#6B9FB5">
  1450. <path class="arrow arrow-data" d="M 900 70 L 900 105 L 550 105 L 550 430"/>
  1451. </g>
  1452. <!-- ad_api → 腾讯 -->
  1453. <g style="color:#F25F5C">
  1454. <path class="arrow" d="M 1050 430 L 1050 105 L 460 105 L 460 70" stroke-dasharray="4 4"/>
  1455. </g>
  1456. <!-- Legend -->
  1457. <g transform="translate(900, 530)">
  1458. <text x="0" y="0" font-family="JetBrains Mono" font-size="10" fill="#94A3B0">EXECUTION_ENABLED=False</text>
  1459. <text x="0" y="14" font-family="JetBrains Mono" font-size="10" fill="#F25F5C">→ ad_api 仅 log,不真改</text>
  1460. </g>
  1461. </svg>
  1462. </section>
  1463. <!-- ====== 08 · DEPLOYMENT ====== -->
  1464. <section id="deployment">
  1465. <div class="section-head">
  1466. <div>
  1467. <div class="section-num">§ 08 — 部署形态</div>
  1468. <h2 class="section-title rules">两种<em>运行模式</em></h2>
  1469. </div>
  1470. <p class="section-desc">本地交互(REPL,人工触发分析)+ K8s CronJob(每日 02:00 UTC 自动跑)。共享同一份代码 + skill + tool。</p>
  1471. </div>
  1472. <div class="deploy-grid">
  1473. <div class="deploy-card">
  1474. <div class="deploy-mode">Mode A · Interactive</div>
  1475. <div class="deploy-name">run.py · REPL</div>
  1476. <p class="deploy-desc">手工启动 Agent,交互式问答。适合临时分析单个广告 / 修改决策 / 调试 skill。Agent 在每轮等待用户输入,可执行任意工具。</p>
  1477. <div class="deploy-files">
  1478. <code><span>$</span> .venv/bin/python3 run.py</code>
  1479. </div>
  1480. </div>
  1481. <div class="deploy-card">
  1482. <div class="deploy-mode">Mode B · Batch</div>
  1483. <div class="deploy-name">execute_once.py · 单次执行</div>
  1484. <p class="deploy-desc">一次性走完 10 步流水线,自动取 T-1(昨天)作为数据日期。K8s CronJob 直接调度,可手工调试。完成后退出。</p>
  1485. <div class="deploy-files">
  1486. <code><span>$</span> .venv/bin/python3 execute_once.py</code>
  1487. <code><span>cron</span> 0 2 * * * UTC (每日 02:00)</code>
  1488. </div>
  1489. </div>
  1490. </div>
  1491. <div style="margin-top: 32px; padding: 24px 28px; background: var(--bg-alt); border-left: 3px solid var(--gold); font-size: 13.5px; color: var(--text-dim); line-height: 1.7;">
  1492. <strong style="color: var(--gold); font-family: var(--mono); font-size: 11px; letter-spacing: 0.15em; text-transform: uppercase; display: block; margin-bottom: 8px;">K8s 资源</strong>
  1493. <span style="color: var(--text);">deployment.yaml</span> · 1 副本 + Service + PVC(挂 /app/outputs)<br>
  1494. <span style="color: var(--text);">cronjob.yaml</span> · 02:00 UTC + forbidConcurrent + 2h deadline<br>
  1495. <span style="color: var(--text);">configmap.yaml</span> · 公开环境变量<br>
  1496. <span style="color: var(--text);">secret.yaml</span> · ODPS / Feishu / 腾讯广告 凭证<br>
  1497. <span style="color: var(--text);">network-policy.yaml</span> · 限制 Pod 出入流量
  1498. </div>
  1499. </section>
  1500. <!-- ====== 09 · 输出层 ====== -->
  1501. <section id="outputs">
  1502. <div class="section-head">
  1503. <div>
  1504. <div class="section-num">§ 09 — 输出物</div>
  1505. <h2 class="section-title skills">每次执行<em>留下的痕迹</em></h2>
  1506. </div>
  1507. <p class="section-desc">所有中间产物均落盘到 <code>outputs/</code>,可独立回放或调试。生产环境通过 PVC 持久化。</p>
  1508. </div>
  1509. <div class="tools-table">
  1510. <div class="tools-row head">
  1511. <div>路径</div>
  1512. <div>类型</div>
  1513. <div>用途</div>
  1514. <div>持久化</div>
  1515. </div>
  1516. <div class="tools-row">
  1517. <div class="tool-file">outputs/raw/</div>
  1518. <div class="tool-cat data">CSV (per day)</div>
  1519. <div class="tool-fn">ODPS 创意级原始数据。增量缓存,已存在跳过。</div>
  1520. <div class="tool-step">14 天</div>
  1521. </div>
  1522. <div class="tools-row">
  1523. <div class="tool-file">outputs/merged/</div>
  1524. <div class="tool-cat data">CSV (per day)</div>
  1525. <div class="tool-fn">合并多日 + 广告状态快照。下游 ROI 计算的输入。</div>
  1526. <div class="tool-step">30 天</div>
  1527. </div>
  1528. <div class="tools-row">
  1529. <div class="tool-file">outputs/metrics_*.csv</div>
  1530. <div class="tool-fn"><code class="tool-cat compute">CSV (latest)</code></div>
  1531. <div class="tool-fn">广告级指标:动态 ROI 7 日均值、cost_7d_avg、ad_age_days、creative_count 等 30+ 字段。</div>
  1532. <div class="tool-step">每日</div>
  1533. </div>
  1534. <div class="tools-row">
  1535. <div class="tool-file">outputs/portfolio_summary/</div>
  1536. <div class="tool-cat compute">JSON</div>
  1537. <div class="tool-fn">渠道 P25/P50/P75 + tier 级 fission/CTR/bid 均值。</div>
  1538. <div class="tool-step">每日</div>
  1539. </div>
  1540. <div class="tools-row">
  1541. <div class="tool-file">outputs/reports/llm_decisions_*.csv</div>
  1542. <div class="tool-cat decide">CSV</div>
  1543. <div class="tool-fn">LLM 输出 + 规则补全的完整决策表。20+ 字段。</div>
  1544. <div class="tool-step">每日</div>
  1545. </div>
  1546. <div class="tools-row">
  1547. <div class="tool-file">outputs/reports/validated_decisions_*.csv</div>
  1548. <div class="tool-cat decide">CSV</div>
  1549. <div class="tool-fn">护栏校验后的决策表(失败的强制改 hold)。</div>
  1550. <div class="tool-step">每日</div>
  1551. </div>
  1552. <div class="tools-row">
  1553. <div class="tool-file">outputs/approvals/{request_id}/</div>
  1554. <div class="tool-cat execute">XLSX + JSON</div>
  1555. <div class="tool-fn">飞书审批表 + 请求/响应缓存。</div>
  1556. <div class="tool-step">永久</div>
  1557. </div>
  1558. <div class="tools-row">
  1559. <div class="tool-file">outputs/execution_log/</div>
  1560. <div class="tool-cat execute">CSV</div>
  1561. <div class="tool-fn">执行审计:每条决策的 API 调用结果。</div>
  1562. <div class="tool-step">永久</div>
  1563. </div>
  1564. <div class="tools-row">
  1565. <div class="tool-file">.trace/{trace_id}/</div>
  1566. <div class="tool-cat report">JSON</div>
  1567. <div class="tool-fn">框架 Trace 持久化:meta + GoalTree + 所有 messages + events。</div>
  1568. <div class="tool-step">永久</div>
  1569. </div>
  1570. </div>
  1571. </section>
  1572. <footer>
  1573. <p>auto_put_ad_mini · system architecture map · v0.4.27</p>
  1574. <p>generated by structural analysis from agent/ + examples/auto_put_ad_mini/ source</p>
  1575. <span class="stamp">作为 Monitor/调控 Agent 的最小业务单元 · 后续将接入 auto_put_ad 完整体系</span>
  1576. </footer>
  1577. </main>
  1578. <script>
  1579. // Subtle scroll-triggered fade for sections
  1580. const observer = new IntersectionObserver((entries) => {
  1581. entries.forEach(entry => {
  1582. if (entry.isIntersecting) {
  1583. entry.target.style.opacity = '1';
  1584. entry.target.style.transform = 'translateY(0)';
  1585. }
  1586. });
  1587. }, { threshold: 0.1, rootMargin: '0px 0px -100px 0px' });
  1588. document.querySelectorAll('section').forEach(s => {
  1589. s.style.opacity = '0';
  1590. s.style.transform = 'translateY(30px)';
  1591. s.style.transition = 'opacity 0.8s ease-out, transform 0.8s ease-out';
  1592. observer.observe(s);
  1593. });
  1594. // Pipeline step hover effect: dim non-hovered
  1595. const steps = document.querySelectorAll('.pipeline-step');
  1596. steps.forEach(step => {
  1597. step.addEventListener('mouseenter', () => {
  1598. steps.forEach(s => { if (s !== step) s.style.opacity = '0.45'; });
  1599. });
  1600. step.addEventListener('mouseleave', () => {
  1601. steps.forEach(s => { s.style.opacity = '1'; });
  1602. });
  1603. });
  1604. </script>
  1605. </body>
  1606. </html>