| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- import {
- Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
- AlignmentType, HeadingLevel, BorderStyle, WidthType, ShadingType,
- VerticalAlign, LevelFormat, ExternalHyperlink, Header, Footer, PageNumber
- } from "docx";
- import { writeFileSync } from "fs";
- // ── Colors ───────────────────────────────────────────────────────────────────
- const CLAUDE_ORANGE = "E8834B";
- const HEADER_BG = "2C3E50";
- const ROW_ALT = "F8F9FA";
- const ROW_HIGHLIGHT = "FFF3CD";
- const BORDER_COLOR = "CCCCCC";
- const WHITE = "FFFFFF";
- const border = (color = BORDER_COLOR) => ({ style: BorderStyle.SINGLE, size: 1, color });
- const allBorders = (color = BORDER_COLOR) => ({
- top: border(color), bottom: border(color),
- left: border(color), right: border(color),
- });
- // ── Helper: table cell ────────────────────────────────────────────────────────
- function cell(text, { bold = false, bg = WHITE, width, align = AlignmentType.LEFT, color = "000000", fontSize = 20 } = {}) {
- return new TableCell({
- borders: allBorders(),
- width: { size: width, type: WidthType.DXA },
- shading: { fill: bg, type: ShadingType.CLEAR },
- verticalAlign: VerticalAlign.CENTER,
- margins: { top: 80, bottom: 80, left: 120, right: 120 },
- children: [new Paragraph({
- alignment: align,
- children: [new TextRun({ text, bold, color, size: fontSize, font: "Arial" })],
- })],
- });
- }
- // ── Helper: header cell (dark bg, white text) ─────────────────────────────────
- function headerCell(text, width) {
- return cell(text, { bold: true, bg: HEADER_BG, color: WHITE, width, fontSize: 20 });
- }
- // ── Helper: section heading ───────────────────────────────────────────────────
- function sectionHeading(text) {
- return new Paragraph({
- spacing: { before: 320, after: 160 },
- children: [new TextRun({ text, bold: true, size: 28, color: "2C3E50", font: "Arial" })],
- border: { bottom: { style: BorderStyle.SINGLE, size: 4, color: CLAUDE_ORANGE, space: 4 } },
- });
- }
- // ── Helper: normal paragraph ─────────────────────────────────────────────────
- function para(text, { bold = false, size = 20, spacing = { after: 120 } } = {}) {
- return new Paragraph({
- spacing,
- children: [new TextRun({ text, bold, size, font: "Arial", color: "333333" })],
- });
- }
- // ── Helper: bullet ────────────────────────────────────────────────────────────
- function bullet(text) {
- return new Paragraph({
- numbering: { reference: "bullets", level: 0 },
- spacing: { after: 80 },
- children: [new TextRun({ text, size: 20, font: "Arial", color: "333333" })],
- });
- }
- // ── Helper: note paragraph ───────────────────────────────────────────────────
- function note(text) {
- return new Paragraph({
- spacing: { before: 100, after: 160 },
- children: [new TextRun({ text: `📌 ${text}`, size: 18, italics: true, color: "555555", font: "Arial" })],
- });
- }
- // ── Section 1: Core pricing table (6 columns) ─────────────────────────────────
- // Content width: A4 9026, use 9000 for slight breathing room
- const COL1 = 1500; // 层级
- const COL2 = 2000; // Claude model
- const COL3 = 1500; // Claude price
- const COL4 = 2000; // OpenAI model
- const COL5 = 1500; // OpenAI price
- const TABLE1_WIDTH = COL1 + COL2 + COL3 + COL4 + COL5; // 8500
- function pricingTable() {
- const rows = [
- ["旗舰", "Claude Opus 4.6", "$5 / $25", "GPT-4o", "$2.50 / $10", false],
- ["标准(推荐)", "Claude Sonnet 4.6", "$3 / $15", "GPT-4o", "$2.50 / $10", true ],
- ["轻量", "Claude Haiku 4.5", "$1 / $5", "GPT-4o mini", "$0.15 / $0.60", false],
- ];
- return new Table({
- width: { size: TABLE1_WIDTH, type: WidthType.DXA },
- columnWidths: [COL1, COL2, COL3, COL4, COL5],
- rows: [
- new TableRow({
- tableHeader: true,
- children: [
- headerCell("层级", COL1),
- headerCell("Claude(Anthropic)", COL2),
- headerCell("价格(输入/输出)", COL3),
- headerCell("ChatGPT(OpenAI)", COL4),
- headerCell("价格(输入/输出)", COL5),
- ],
- }),
- ...rows.map(([tier, claude, cp, oai, op, highlight]) =>
- new TableRow({
- children: [
- cell(tier, { width: COL1, bg: highlight ? ROW_HIGHLIGHT : WHITE, bold: highlight }),
- cell(claude, { width: COL2, bg: highlight ? ROW_HIGHLIGHT : WHITE, bold: highlight, color: highlight ? "C0392B" : "000000" }),
- cell(cp, { width: COL3, bg: highlight ? ROW_HIGHLIGHT : WHITE, bold: highlight, align: AlignmentType.CENTER }),
- cell(oai, { width: COL4, bg: highlight ? ROW_HIGHLIGHT : WHITE }),
- cell(op, { width: COL5, bg: highlight ? ROW_HIGHLIGHT : WHITE, align: AlignmentType.CENTER }),
- ],
- })
- ),
- ],
- });
- }
- // ── Section 2: Cost estimate table ───────────────────────────────────────────
- const E_COL1 = 2200;
- const E_COL2 = 3200;
- const E_COL3 = 3200;
- const TABLE2_WIDTH = E_COL1 + E_COL2 + E_COL3; // 8600
- function costTable() {
- const rows = [
- ["每次对话成本", "~$0.0037", "~$0.0027"],
- ["1,000 次对话/月", "$3.70", "$2.70"],
- ["10,000 次对话/月", "$37", "$27"],
- ["100,000 次对话/月", "$370", "$270"],
- ];
- return new Table({
- width: { size: TABLE2_WIDTH, type: WidthType.DXA },
- columnWidths: [E_COL1, E_COL2, E_COL3],
- rows: [
- new TableRow({
- tableHeader: true,
- children: [
- headerCell("", E_COL1),
- headerCell("Claude Sonnet 4.6", E_COL2),
- headerCell("GPT-4o", E_COL3),
- ],
- }),
- ...rows.map(([label, claude, oai], i) =>
- new TableRow({
- children: [
- cell(label, { width: E_COL1, bg: i % 2 === 0 ? WHITE : ROW_ALT, bold: true }),
- cell(claude, { width: E_COL2, bg: i % 2 === 0 ? WHITE : ROW_ALT, align: AlignmentType.CENTER, color: "C0392B", bold: true }),
- cell(oai, { width: E_COL3, bg: i % 2 === 0 ? WHITE : ROW_ALT, align: AlignmentType.CENTER }),
- ],
- })
- ),
- ],
- });
- }
- // ── Section 3: Feature comparison ────────────────────────────────────────────
- const F_COL1 = 2400;
- const F_COL2 = 3000;
- const F_COL3 = 3000;
- const TABLE3_WIDTH = F_COL1 + F_COL2 + F_COL3; // 8400
- function featureTable(rows) {
- return new Table({
- width: { size: TABLE3_WIDTH, type: WidthType.DXA },
- columnWidths: [F_COL1, F_COL2, F_COL3],
- rows: [
- new TableRow({
- tableHeader: true,
- children: [
- headerCell("功能", F_COL1),
- headerCell("Claude API", F_COL2),
- headerCell("ChatGPT API", F_COL3),
- ],
- }),
- ...rows.map(([feat, claude, oai], i) =>
- new TableRow({
- children: [
- cell(feat, { width: F_COL1, bg: i % 2 === 0 ? WHITE : ROW_ALT, bold: true }),
- cell(claude, { width: F_COL2, bg: i % 2 === 0 ? WHITE : ROW_ALT, color: "1A6B2A" }),
- cell(oai, { width: F_COL3, bg: i % 2 === 0 ? WHITE : ROW_ALT }),
- ],
- })
- ),
- ],
- });
- }
- // ── Section 4: Capability comparison ─────────────────────────────────────────
- const C_COL1 = 2200;
- const C_COL2 = 3200;
- const C_COL3 = 3200;
- const TABLE4_WIDTH = C_COL1 + C_COL2 + C_COL3;
- function capabilityTable(rows) {
- return new Table({
- width: { size: TABLE4_WIDTH, type: WidthType.DXA },
- columnWidths: [C_COL1, C_COL2, C_COL3],
- rows: [
- new TableRow({
- tableHeader: true,
- children: [
- headerCell("维度", C_COL1),
- headerCell("Claude Sonnet 4.6", C_COL2),
- headerCell("GPT-4o", C_COL3),
- ],
- }),
- ...rows.map(([dim, claude, oai], i) =>
- new TableRow({
- children: [
- cell(dim, { width: C_COL1, bg: i % 2 === 0 ? WHITE : ROW_ALT, bold: true }),
- cell(claude, { width: C_COL2, bg: i % 2 === 0 ? WHITE : ROW_ALT }),
- cell(oai, { width: C_COL3, bg: i % 2 === 0 ? WHITE : ROW_ALT }),
- ],
- })
- ),
- ],
- });
- }
- // ── Section 6: Annual forecast ────────────────────────────────────────────────
- const Y_COL1 = 2200;
- const Y_COL2 = 3200;
- const Y_COL3 = 3200;
- const TABLE5_WIDTH = Y_COL1 + Y_COL2 + Y_COL3;
- function yearlyTable() {
- const rows = [
- ["月费估算", "~$30 – $50", "~$25 – $40"],
- ["年费估算", "~$360 – $600", "~$300 – $480"],
- ];
- return new Table({
- width: { size: TABLE5_WIDTH, type: WidthType.DXA },
- columnWidths: [Y_COL1, Y_COL2, Y_COL3],
- rows: [
- new TableRow({
- tableHeader: true,
- children: [
- headerCell("", Y_COL1),
- headerCell("Claude Sonnet 4.6(含缓存)", Y_COL2),
- headerCell("GPT-4o(含缓存)", Y_COL3),
- ],
- }),
- ...rows.map(([label, claude, oai], i) =>
- new TableRow({
- children: [
- cell(label, { width: Y_COL1, bg: i % 2 === 0 ? WHITE : ROW_ALT, bold: true }),
- cell(claude, { width: Y_COL2, bg: i % 2 === 0 ? WHITE : ROW_ALT, align: AlignmentType.CENTER, bold: true, color: "C0392B" }),
- cell(oai, { width: Y_COL3, bg: i % 2 === 0 ? WHITE : ROW_ALT, align: AlignmentType.CENTER }),
- ],
- })
- ),
- ],
- });
- }
- // ── Build document ────────────────────────────────────────────────────────────
- const doc = new Document({
- numbering: {
- config: [
- {
- reference: "bullets",
- levels: [{
- level: 0, format: LevelFormat.BULLET, text: "\u2022",
- alignment: AlignmentType.LEFT,
- style: { paragraph: { indent: { left: 720, hanging: 360 } } },
- }],
- },
- ],
- },
- styles: {
- default: {
- document: { run: { font: "Arial", size: 20, color: "333333" } },
- },
- },
- sections: [{
- properties: {
- page: {
- size: { width: 11906, height: 16838 }, // A4
- margin: { top: 1080, right: 1080, bottom: 1080, left: 1080 },
- },
- },
- headers: {
- default: new Header({
- children: [new Paragraph({
- alignment: AlignmentType.RIGHT,
- border: { bottom: { style: BorderStyle.SINGLE, size: 4, color: CLAUDE_ORANGE, space: 4 } },
- spacing: { after: 200 },
- children: [new TextRun({
- text: "Claude API vs ChatGPT API — 价格对比报告",
- size: 16, color: "888888", font: "Arial",
- })],
- })],
- }),
- },
- footers: {
- default: new Footer({
- children: [new Paragraph({
- alignment: AlignmentType.CENTER,
- border: { top: { style: BorderStyle.SINGLE, size: 2, color: BORDER_COLOR, space: 4 } },
- spacing: { before: 100 },
- children: [
- new TextRun({ text: "数据来源:platform.claude.com / openai.com/api/pricing | 第 ", size: 16, color: "888888", font: "Arial" }),
- new TextRun({ children: [PageNumber.CURRENT], size: 16, color: "888888", font: "Arial" }),
- new TextRun({ text: " 页", size: 16, color: "888888", font: "Arial" }),
- ],
- })],
- }),
- },
- children: [
- // ── Title block ──────────────────────────────────────────────────────
- new Paragraph({
- spacing: { before: 0, after: 80 },
- children: [new TextRun({
- text: "Claude API vs ChatGPT API",
- bold: true, size: 52, color: "2C3E50", font: "Arial",
- })],
- }),
- new Paragraph({
- spacing: { before: 0, after: 60 },
- children: [new TextRun({
- text: "价格对比报告",
- bold: true, size: 36, color: CLAUDE_ORANGE, font: "Arial",
- })],
- }),
- new Paragraph({
- spacing: { after: 320 },
- border: { bottom: { style: BorderStyle.SINGLE, size: 6, color: CLAUDE_ORANGE, space: 6 } },
- children: [new TextRun({ text: "制作日期:2026年4月", size: 18, color: "888888", font: "Arial" })],
- }),
- // ── Section 1 ────────────────────────────────────────────────────────
- sectionHeading("一、核心模型价格对比(每百万 Token)"),
- pricingTable(),
- note("本项目 chatbot 使用 Claude Sonnet 4.6,对标 OpenAI 的 GPT-4o 层级。"),
- // ── Section 2 ────────────────────────────────────────────────────────
- sectionHeading("二、实际对话成本估算"),
- para("以 chatbot 典型对话为例:"),
- bullet("输入:~500 tokens(系统提示 + 对话历史)"),
- bullet("输出:~150 tokens(Bot 回复)"),
- new Paragraph({ spacing: { after: 120 } }),
- costTable(),
- note("月差额约 $10–$100,对企业级系统而言可忽略不计。"),
- // ── Section 3 ────────────────────────────────────────────────────────
- sectionHeading("三、成本优化功能对比"),
- featureTable([
- ["Batch 批处理折扣", "标准价 5折", "标准价 5折"],
- ["Prompt Caching", "命中缓存仅需原价 10%", "命中缓存 50%"],
- ["Context 窗口", "200K tokens", "128K tokens"],
- ["最长单次输入", "更适合长文档/历史记录", "受限于 128K"],
- ]),
- note("Prompt Caching:本项目系统提示词(Ellie 角色设定)每次对话都会重复发送,Claude 缓存命中价格仅 $0.30/百万 tokens(原价 $3 的 10%),长期运行下成本优势显著。"),
- // ── Section 4 ────────────────────────────────────────────────────────
- sectionHeading("四、功能与定位对比"),
- capabilityTable([
- ["中文理解质量", "优秀", "优秀"],
- ["长上下文处理", "200K tokens(领先)", "128K tokens"],
- ["代码生成", "优秀", "优秀"],
- ["内容安全合规", "Constitutional AI,策略更严谨", "中等"],
- ["响应稳定性", "更一致", "中等"],
- ["企业 SLA", "有", "有"],
- ["数据隐私", "不用于模型训练", "不用于模型训练"],
- ["API 成熟度", "成熟", "成熟"],
- ]),
- // ── Section 5 ────────────────────────────────────────────────────────
- sectionHeading("五、推荐选择 Claude API 的理由"),
- para("1. 成本差异极小,价值更高", { bold: true }),
- para("对于本项目规模(B2B dealer chatbot),两者月费差额通常在 $10–$50 以内,而 Claude 在长文档处理和一致性上表现更好。"),
- para("2. Prompt Caching 优势明显", { bold: true }),
- para("本项目系统提示词固定(Ellie 角色设定 + ERP 数据上下文),每次对话都能命中缓存,实际输入成本可降至标准价的 10%,有效抵消价格差。"),
- para("3. 200K 上下文窗口", { bold: true }),
- para("随着 ERP 数据集成(订单详情、产品目录),注入 LLM 的 context 数据量会增加。200K 窗口提供更大余量,无需担心超出限制。"),
- para("4. 技术栈已完成集成", { bold: true }),
- para("项目代码已使用 claude-sonnet-4-6 完成集成,切换到 ChatGPT API 需要额外改造工作和测试成本。"),
- para("5. 内容安全合规", { bold: true }),
- para("Anthropic 的 Constitutional AI 在拒绝不适当请求、保持回复边界方面更可靠,适合面向 dealer 的 B2B 场景。"),
- // ── Section 6 ────────────────────────────────────────────────────────
- sectionHeading("六、费用预测(年度)"),
- para("假设每月 20,000 次对话,启用 Prompt Caching:"),
- yearlyTable(),
- note("两者年费差额约 $60–$120,可忽略不计。"),
- // ── Conclusion ───────────────────────────────────────────────────────
- new Paragraph({
- spacing: { before: 320, after: 160 },
- shading: { fill: "EBF5FB", type: ShadingType.CLEAR },
- border: {
- top: { style: BorderStyle.SINGLE, size: 4, color: CLAUDE_ORANGE },
- bottom: { style: BorderStyle.SINGLE, size: 4, color: CLAUDE_ORANGE },
- left: { style: BorderStyle.THICK, size: 12, color: CLAUDE_ORANGE },
- right: { style: BorderStyle.SINGLE, size: 4, color: CLAUDE_ORANGE },
- },
- children: [new TextRun({
- text: "结论:推荐使用 Claude API(Anthropic)",
- bold: true, size: 26, color: "2C3E50", font: "Arial",
- })],
- }),
- para("在价格接近的前提下,Claude 提供更大的 Context 窗口、更优的 Prompt Caching 折扣,且代码已完成集成,综合性价比更高,是本项目的最优选择。"),
- // ── Sources ──────────────────────────────────────────────────────────
- new Paragraph({
- spacing: { before: 400, after: 80 },
- border: { top: { style: BorderStyle.SINGLE, size: 2, color: BORDER_COLOR, space: 6 } },
- children: [new TextRun({ text: "数据来源", bold: true, size: 18, color: "888888", font: "Arial" })],
- }),
- new Paragraph({
- spacing: { after: 60 },
- children: [
- new TextRun({ text: "Anthropic Claude API Pricing: ", size: 16, color: "888888", font: "Arial" }),
- new ExternalHyperlink({
- link: "https://platform.claude.com/docs/en/about-claude/pricing",
- children: [new TextRun({ text: "platform.claude.com/docs/en/about-claude/pricing", size: 16, style: "Hyperlink", font: "Arial" })],
- }),
- ],
- }),
- new Paragraph({
- spacing: { after: 60 },
- children: [
- new TextRun({ text: "OpenAI API Pricing: ", size: 16, color: "888888", font: "Arial" }),
- new ExternalHyperlink({
- link: "https://openai.com/api/pricing/",
- children: [new TextRun({ text: "openai.com/api/pricing", size: 16, style: "Hyperlink", font: "Arial" })],
- }),
- ],
- }),
- ],
- }],
- });
- const outPath = "C:\\Codes\\homelegance-chatbot\\deploy\\Claude_vs_ChatGPT_API价格对比.docx";
- Packer.toBuffer(doc).then(buf => {
- writeFileSync(outPath, buf);
- console.log("Created:", outPath);
- });
|