Initial commit: LangChain evolution tutorial deck (132 slides)
- Cover, TOC, 5 dividers, 3 recap slides - 5 sections (chains, langgraph, deepagents, openswe, ecosystem) - design-system.js with theme tokens + 9 helper functions - research/: timeline + sources + per-tech notes - final-compile.js + merge.js for rebuild pipeline - output/: langchain-evolution.pptx (2.3 MB) + langchain-evolution.pdf (1.1 MB) + 7 sample previews
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
/**
|
||||
* final-compile.js
|
||||
* Создаёт:
|
||||
* - build/intro.pptx (cover + TOC)
|
||||
* - build/dividers.pptx (5 dividers между секциями)
|
||||
* - build/recap.pptx (3 recap слайда: timeline, what's next, closing)
|
||||
* Затем вызывает merge.js для склейки всех в один .pptx.
|
||||
*/
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const ds = require('./design-system');
|
||||
const { theme, palette, fonts, sizes, layouts, helpers } = ds;
|
||||
const pptxgen = require('pptxgenjs');
|
||||
|
||||
const WORKSPACE = __dirname;
|
||||
const SLIDES = path.join(WORKSPACE, 'slides');
|
||||
const BUILD = path.join(WORKSPACE, 'build');
|
||||
fs.mkdirSync(BUILD, { recursive: true });
|
||||
|
||||
const SECTIONS = [
|
||||
{ dir: 'section1-chains', title: 'LangChain 1.0', subtitle: 'chains, LCEL, agents, retrievers' },
|
||||
{ dir: 'section2-langgraph', title: 'LangGraph 1.0', subtitle: 'state, nodes, persistence, HITL' },
|
||||
{ dir: 'section3-deepagents', title: 'Deep Agents', subtitle: 'harness, todos, virtual FS, subagents' },
|
||||
{ dir: 'section4-openswe', title: 'Open SWE', subtitle: 'async coding agent, triggers, dashboard' },
|
||||
{ dir: 'section5-ecosystem', title: 'Экосистема', subtitle: 'LangSmith, Studio, deployment' },
|
||||
];
|
||||
|
||||
// -------- INTRO (cover + TOC) --------
|
||||
function buildIntro(pres) {
|
||||
// Slide 1 -- cover
|
||||
const s1 = pres.addSlide();
|
||||
helpers.slideBase(s1, pres, theme);
|
||||
// top accent
|
||||
s1.addShape(pres.ShapeType.rect, { x: 0, y: 0, w: 10, h: 0.15, fill: { color: palette.accent.primary }, line: { type: 'none' } });
|
||||
helpers.addHeader(s1, pres, theme, {
|
||||
eyebrow: 'DEEP DIVE / TUTORIAL',
|
||||
sectionNumber: 0,
|
||||
title: 'Эволюция LangChain',
|
||||
});
|
||||
// subtitle
|
||||
s1.addText('от chains до Deep Agents и Open SWE', {
|
||||
x: 0.5, y: 1.85, w: 9, h: 0.5,
|
||||
fontFace: fonts.ui, fontSize: 24, color: palette.text.secondary, bold: false,
|
||||
});
|
||||
// gold bar
|
||||
s1.addShape(pres.ShapeType.rect, { x: 0.5, y: 2.55, w: 1.5, h: 0.06, fill: { color: palette.accent.secondary }, line: { type: 'none' } });
|
||||
// description
|
||||
s1.addText(
|
||||
'Большой tutorial по экосистеме LangChain: chains, LCEL, LangGraph, Deep Agents, Open SWE. ' +
|
||||
'Python 1.0+, реальные API, плотный код.',
|
||||
{
|
||||
x: 0.5, y: 2.85, w: 9, h: 1.5,
|
||||
fontFace: fonts.ui, fontSize: 16, color: palette.text.secondary,
|
||||
}
|
||||
);
|
||||
// footer meta
|
||||
s1.addText('100+ слайдов | 2022 -- 2026 | Python 1.0+', {
|
||||
x: 0.5, y: 5.15, w: 9, h: 0.3,
|
||||
fontFace: fonts.ui, fontSize: 11, color: palette.text.muted, align: 'center',
|
||||
});
|
||||
helpers.addPageNumber(s1, pres, theme, 1);
|
||||
|
||||
// Slide 2 -- TOC
|
||||
const s2 = pres.addSlide();
|
||||
helpers.slideBase(s2, pres, theme);
|
||||
helpers.addHeader(s2, pres, theme, {
|
||||
eyebrow: 'CONTENTS',
|
||||
sectionNumber: 0,
|
||||
title: 'Содержание',
|
||||
});
|
||||
let y = layouts.CONTENT_TOP + 0.1;
|
||||
SECTIONS.forEach((sec, i) => {
|
||||
// card
|
||||
s2.addShape(pres.ShapeType.roundRect, {
|
||||
x: 0.5, y: y, w: 9, h: 0.55,
|
||||
fill: { color: palette.bg.elevated },
|
||||
line: { color: palette.border.subtle, width: 0.5 },
|
||||
rectRadius: 0.08,
|
||||
});
|
||||
// number badge
|
||||
s2.addShape(pres.ShapeType.roundRect, {
|
||||
x: 0.65, y: y + 0.1, w: 0.35, h: 0.35,
|
||||
fill: { color: palette.accent.primary },
|
||||
line: { type: 'none' },
|
||||
rectRadius: 0.04,
|
||||
});
|
||||
s2.addText(String(i + 1), {
|
||||
x: 0.65, y: y + 0.12, w: 0.35, h: 0.3,
|
||||
fontFace: fonts.ui, fontSize: 14, color: palette.bg.primary, bold: true, align: 'center',
|
||||
});
|
||||
s2.addText(sec.title + ' -- ' + sec.subtitle, {
|
||||
x: 1.15, y: y + 0.08, w: 7.5, h: 0.4,
|
||||
fontFace: fonts.ui, fontSize: 14, color: palette.text.primary, bold: true,
|
||||
});
|
||||
y += 0.65;
|
||||
});
|
||||
// total
|
||||
s2.addText('Всего: 122 content слайдов + cover, TOC, 5 dividers, recap = 132', {
|
||||
x: 0.5, y: 5.05, w: 9, h: 0.3,
|
||||
fontFace: fonts.ui, fontSize: 12, color: palette.accent.secondary, align: 'center',
|
||||
});
|
||||
helpers.addPageNumber(s2, pres, theme, 2);
|
||||
}
|
||||
|
||||
// -------- DIVIDERS (5) --------
|
||||
function buildDividers(pres) {
|
||||
SECTIONS.forEach((sec, i) => {
|
||||
const slide = pres.addSlide();
|
||||
helpers.slideBase(slide, pres, theme);
|
||||
// big stage number
|
||||
slide.addShape(pres.ShapeType.rect, {
|
||||
x: 0, y: 0, w: 10, h: 0.15,
|
||||
fill: { color: palette.accent.primary }, line: { type: 'none' },
|
||||
});
|
||||
// huge number
|
||||
slide.addText('STAGE ' + (i + 1), {
|
||||
x: 0.5, y: 1.4, w: 9, h: 0.5,
|
||||
fontFace: fonts.ui, fontSize: 18, color: palette.accent.primary, bold: true, charSpacing: 6,
|
||||
});
|
||||
slide.addText(sec.title, {
|
||||
x: 0.5, y: 2.0, w: 9, h: 1.2,
|
||||
fontFace: fonts.ui, fontSize: 60, color: palette.text.primary, bold: true,
|
||||
});
|
||||
slide.addText(sec.subtitle, {
|
||||
x: 0.5, y: 3.2, w: 9, h: 0.5,
|
||||
fontFace: fonts.ui, fontSize: 22, color: palette.text.secondary,
|
||||
});
|
||||
// gold accent line
|
||||
slide.addShape(pres.ShapeType.rect, {
|
||||
x: 0.5, y: 3.85, w: 1.5, h: 0.05,
|
||||
fill: { color: palette.accent.secondary }, line: { type: 'none' },
|
||||
});
|
||||
// section nav
|
||||
slide.addText('Раздел ' + (i + 1) + ' из ' + SECTIONS.length, {
|
||||
x: 0.5, y: 5.0, w: 9, h: 0.3,
|
||||
fontFace: fonts.ui, fontSize: 12, color: palette.text.muted, align: 'center',
|
||||
});
|
||||
helpers.addPageNumber(slide, pres, theme, 0); // 0 = no number
|
||||
});
|
||||
}
|
||||
|
||||
// -------- RECAP (3) --------
|
||||
function buildRecap(pres) {
|
||||
// Recap 1 -- Timeline
|
||||
let s = pres.addSlide();
|
||||
helpers.slideBase(s, pres, theme);
|
||||
helpers.addHeader(s, pres, theme, {
|
||||
eyebrow: 'TIMELINE',
|
||||
sectionNumber: 0,
|
||||
title: 'Полная карта эволюции',
|
||||
});
|
||||
s.addText(
|
||||
[
|
||||
{ text: '2022-10 ', options: { color: palette.accent.primary, bold: true } },
|
||||
{ text: 'LangChain 0.1 -- запуск фреймворка chains\n', options: { color: palette.text.primary } },
|
||||
{ text: '2023-10 ', options: { color: palette.accent.primary, bold: true } },
|
||||
{ text: 'LangChain 0.1 (stable) -- первый production-ready\n', options: { color: palette.text.primary } },
|
||||
{ text: '2024-01 ', options: { color: palette.accent.primary, bold: true } },
|
||||
{ text: 'LangGraph 0.1 -- stateful графы\n', options: { color: palette.text.primary } },
|
||||
{ text: '2025-08 ', options: { color: palette.accent.primary, bold: true } },
|
||||
{ text: 'Deep Agents 0.x -- harness с subagents\n', options: { color: palette.text.primary } },
|
||||
{ text: '2025-10 ', options: { color: palette.accent.secondary, bold: true } },
|
||||
{ text: 'LangChain 1.0 + LangGraph 1.0 (22.10.2025)\n', options: { color: palette.text.primary } },
|
||||
{ text: '2025-12 ', options: { color: palette.accent.secondary, bold: true } },
|
||||
{ text: 'Open SWE 1.0 -- async coding agent', options: { color: palette.text.primary } },
|
||||
],
|
||||
{
|
||||
x: 0.7, y: 1.5, w: 8.6, h: 3.4,
|
||||
fontFace: 'JetBrains Mono', fontSize: 13, color: palette.text.primary,
|
||||
paraSpaceAfter: 8,
|
||||
}
|
||||
);
|
||||
helpers.addPageNumber(s, pres, theme, 130);
|
||||
|
||||
// Recap 2 -- What's next
|
||||
s = pres.addSlide();
|
||||
helpers.slideBase(s, pres, theme);
|
||||
helpers.addHeader(s, pres, theme, {
|
||||
eyebrow: 'WHAT IS NEXT',
|
||||
sectionNumber: 0,
|
||||
title: 'Куда движется стек',
|
||||
});
|
||||
const items = [
|
||||
['Subagents', 'делегирование доменных задач с собственным state'],
|
||||
['Virtual filesystem', 'stateful scratchpad для reasoning-агентов'],
|
||||
['Human-in-the-loop', 'production-ready approval flows в LangGraph 1.0+'],
|
||||
['Open SWE', 'async coding agent с GitHub-триггерами и dashboard'],
|
||||
['LangSmith', 'observability: traces, evals, online monitoring'],
|
||||
];
|
||||
let y = 1.5;
|
||||
items.forEach(([k, v]) => {
|
||||
s.addText(k, {
|
||||
x: 0.7, y: y, w: 3, h: 0.4,
|
||||
fontFace: fonts.ui, fontSize: 16, color: palette.accent.secondary, bold: true,
|
||||
});
|
||||
s.addText(v, {
|
||||
x: 3.8, y: y + 0.05, w: 5.7, h: 0.4,
|
||||
fontFace: fonts.ui, fontSize: 13, color: palette.text.primary,
|
||||
});
|
||||
y += 0.55;
|
||||
});
|
||||
helpers.addPageNumber(s, pres, theme, 131);
|
||||
|
||||
// Recap 3 -- Closing
|
||||
s = pres.addSlide();
|
||||
helpers.slideBase(s, pres, theme);
|
||||
helpers.addHeader(s, pres, theme, {
|
||||
eyebrow: 'CLOSING',
|
||||
sectionNumber: 0,
|
||||
title: 'Главный тренд',
|
||||
});
|
||||
s.addText(
|
||||
'От chain-of-prompts к stateful агентам с harness и human-in-the-loop.',
|
||||
{
|
||||
x: 0.7, y: 1.7, w: 8.6, h: 0.8,
|
||||
fontFace: fonts.ui, fontSize: 22, color: palette.accent.secondary, bold: true,
|
||||
}
|
||||
);
|
||||
s.addText(
|
||||
'LangChain 1.0 -- это stable ядро (chains, LCEL, agents, retrievers). ' +
|
||||
'LangGraph 1.0 -- stateful execution layer. ' +
|
||||
'Deep Agents -- reasoning harness "из коробки". ' +
|
||||
'Open SWE -- продуктовая реализация coding agent. ' +
|
||||
'Всё это работает на Python 1.0+ с @traceable из LangSmith.',
|
||||
{
|
||||
x: 0.7, y: 2.7, w: 8.6, h: 1.8,
|
||||
fontFace: fonts.ui, fontSize: 14, color: palette.text.primary,
|
||||
}
|
||||
);
|
||||
s.addText('Спасибо! | Mavis / 2026 / Python 1.0+', {
|
||||
x: 0.7, y: 5.0, w: 8.6, h: 0.3,
|
||||
fontFace: fonts.ui, fontSize: 12, color: palette.text.muted, align: 'center',
|
||||
});
|
||||
helpers.addPageNumber(s, pres, theme, 132);
|
||||
}
|
||||
|
||||
// -------- MAIN --------
|
||||
async function main() {
|
||||
// intro
|
||||
let p = new pptxgen();
|
||||
p.layout = 'LAYOUT_WIDE'; // 13.33x7.5 -- not used since helpers override
|
||||
p.defineLayout({ name: 'LC_16x9', width: 10, height: 5.625 });
|
||||
p.layout = 'LC_16x9';
|
||||
buildIntro(p);
|
||||
const introPath = path.join(BUILD, 'intro.pptx');
|
||||
await p.writeFile({ fileName: introPath });
|
||||
console.log('[final-compile] wrote ' + introPath);
|
||||
|
||||
// dividers
|
||||
p = new pptxgen();
|
||||
p.defineLayout({ name: 'LC_16x9', width: 10, height: 5.625 });
|
||||
p.layout = 'LC_16x9';
|
||||
buildDividers(p);
|
||||
const divPath = path.join(BUILD, 'dividers.pptx');
|
||||
await p.writeFile({ fileName: divPath });
|
||||
console.log('[final-compile] wrote ' + divPath);
|
||||
|
||||
// recap
|
||||
p = new pptxgen();
|
||||
p.defineLayout({ name: 'LC_16x9', width: 10, height: 5.625 });
|
||||
p.layout = 'LC_16x9';
|
||||
buildRecap(p);
|
||||
const recapPath = path.join(BUILD, 'recap.pptx');
|
||||
await p.writeFile({ fileName: recapPath });
|
||||
console.log('[final-compile] wrote ' + recapPath);
|
||||
}
|
||||
|
||||
main().catch((e) => { console.error(e); process.exit(1); });
|
||||
Reference in New Issue
Block a user