Mã nguồn mở  ·  MIT

Một chiến dịch chạy từ brief tới báo cáo —
AI làm, bạn duyệt.

Repo này là engine: script, cổng kiểm, quy trình và template. Nội dung, dữ liệu và báo cáo của bạn sống ở một trạm riêng, nằm ngoài git — chỗ nào là do bạn chọn.

Dry-run mặc định MIT License Markdown là nguồn Python 3.10+
Tính năng

Agent chạy nhanh, nhưng chỉ đi qua chỗ bạn đã mở cửa

Tự động hoá marketing thường hỏng ở một chỗ: không ai biết cái gì đã được duyệt, cái gì chỉ là bản nháp trôi ra ngoài. Ở đây trạng thái nằm trong file, và file thì đọc được bằng mắt.

Mở bằng mắt, diff bằng git

Mỗi chiến dịch là một file Markdown: brief ở đầu, bảng danh sách bài ở giữa, quyết định và báo cáo ở cuối. Mỗi bài là một thư mục. Không có định dạng nhị phân nào ở giữa bạn và dữ liệu của bạn — mở lên là đọc được, sửa một chữ thì git chỉ ra đúng chữ đó, và không ai khoá file của ai.

campaign.md · research.md · content.md · publish.json

Ba chỗ máy không được tự quyết

Cổng 1 — chọn đề tài: script tạo bài ở trạng thái proposed với ô duyệt để trống. Không có đường nào trong mã tự đặt thành đã duyệt.
Cổng 2 — trước khi dựng: đọc bài rồi mới quyết. Cổng nằm trước khâu dựng tiếng và hình, vì đó là chỗ tốn tiền — bác ở đây thì chưa mất một giây nào.
Cổng 3 — bản thật: mở đúng trang người đọc sẽ thấy, không phải một bản markdown rồi hy vọng nó render đúng.

proposed → approved → published

Duyệt bằng điện thoại, không cần mở máy

Cổng nào tới hạn thì bot nhắn cho bạn: bấm nút để duyệt, hoặc trả lời thẳng vào tin đó bằng nhận xét tự do — không cần nhớ mã bài, không cần đúng cú pháp. Nhận xét đi vào phan-hoi.md của chính bài, bộ viết đọc rồi sửa lại.

Nhận xét không phải là gật đầu. Góp ý chỉ mở vòng sửa, không mở cổng — nhầm chiều là đăng đúng bài đang bị chê. Và chữ của người luôn đi qua file trong khối có rào, không đi qua dòng lệnh: nó là dữ liệu, không phải mệnh lệnh cho hệ thống.

duyệt từng bài · hoặc theo lô 5–10 bài một lượt

Đăng lên web của bạn, không phải web của tôi

Bộ đăng web đọc đích từ channel.yml — repo nào, thư mục nào, lệnh dựng lại mục lục nào, kiểm bằng cách nào. Không khai thì script dừng, không đoán bừa một chỗ để ghi. Đổi trang web là sửa cấu hình, không phải sửa mã.

Sau khi đẩy, script gọi lại URL và bắt buộc thấy 200 mới dám báo thành công — “đã push” không đồng nghĩa với “trang đã sống”. Và nó chỉ git add đúng những đường dẫn đã khai, không bao giờ -A: máy bạn có thể đang chạy việc khác cùng lúc.

web_target: repo · content_dir · post_cmd · verify

Dry-run là mặc định

Script đăng bài đọc autonomy trong channel.ymlthoát với mã 4 nếu chưa phải full — không gọi API. Không tìm thấy file cấu hình cũng vậy: không biết mức tự trị mà vẫn đăng là đúng kiểu lỗi tệ nhất. Ba mức: suggest chỉ đọc và đề xuất, auto_safe được viết nháp, full mới được đăng. Đổi mức là việc của người, không phải của agent.

23 cổng kiểm bằng số

Độ dài bài, số nguồn đã đối chiếu, link còn sống hay đã chết, chữ mẫu còn sót, tên công cụ nội bộ lọt ra bản công khai — mỗi thứ một cổng, trả về một con số so với một ngưỡng. Trạng thái thứ ba là “thiếu”: cổng không chạy được thì không bao giờ được tính là đạt. Không kiểm được là chưa biết, không phải đã qua.

Không đo thì không kết luận

Baseline là median của 10 nội dung gần nhất cùng định dạng, trên chính kênh của bạn — không phải “chuẩn ngành” đọc ở đâu đó. Chưa đủ mẫu thì hiện “chưa đủ mẫu”, không hiện một con số đoán.

Bộ viết là của bạn, repo không khoá CLI nào

Repo này không gọi thẳng một trợ lý AI nào. Bạn khai lệnh của mình vào runtime.writer_cmd — Claude, Codex, một script Python, hay một người thật ngồi viết cũng được. Hợp đồng chỉ có hai vế: vào là thư mục bài, ra là content.md đã điền theo neo. Không khai thì bước soạn báo chờ người viết và dừng.

Mã thoát 0 không đủ để được tính là xong. Hệ kiểm lại bài có chữ thật không — bộ viết chạy êm mà file vẫn trống thì vẫn tính là hỏng. Đó là hình dạng hỏng nguy hiểm nhất: chỉ tin mã thoát thì bài rỗng đi thẳng tới bước đăng.

writer_cmd: '<lệnh của bạn> --bai "{bai}"'

Một trạm mẫu đã điền sẵn

Thư mục examples/ là một trạm hoàn chỉnh thu nhỏ: một kênh, một chiến dịch, ba bài ở ba trạng thái — đã đăng, đã duyệt đề tài, mới đề xuất. Nó trả lời câu hỏi mà một file mẫu rỗng không trả lời được: điền xong thì trông như thế nào? Kèm cả một báo cáo cổng đang đỏ, để bạn thấy lúc hỏng nó ra sao chứ không chỉ thấy lúc mọi thứ xanh. Clone về là chạy, không cần token, không đăng gì lên đâu. Bộ dữ liệu ngân hàng giả định ở content/KPIM vẫn còn, dùng cho phần dạy học.

examples/ · 3 trạng thái 322 test · 23 cổng Không PII · không credential Không đăng gì lên đâu
Cách hoạt động

Sáu bước, ba chỗ dừng chờ người

Agent đi liền mạch từ brief tới báo cáo, nhưng dừng ở ba chỗ: chọn đề tài, trước khi dựngtrước khi phát. Duyệt bằng cách điền vào file, hoặc trả lời ngay trên Telegram — cùng một trạng thái, hai đường vào. Bỏ trống thì đường ống đứng yên: không có “mặc định đồng ý”, và không có đường nào trong mã tự điền hộ.

01

Brief chiến dịch

Mục tiêu, đối tượng, thông điệp, trụ nội dung — điền vào campaign.md. Chưa đủ thì script không cho tạo bài.

02

Đề xuất bài

Agent đối chiếu sổ bài cũ để khỏi trùng đề tài, rồi thêm dòng vào bảng Content ở trạng thái đề xuất.

03

Nghiên cứu & viết

Chỉ bài đã qua Cổng 1 mới được viết. Nguồn vào research.mdđóng băng ở đó; bài vào content.md.

04

23 cổng, rồi bạn duyệt

Máy kiểm hình thức bằng số. Còn câu “bài này có đáng đăng không” thì không cổng nào trả lời được — đó là Cổng 2, và nó nằm trước khâu dựng tiếng và hình để bài bị bác không tốn một giây render nào.

05

Dựng, lên web, rồi phát

Audio và ảnh dựng xong thì bài lên trang web trước để bạn xem bản thật — đó là Cổng 3. Duyệt rồi mới tới YouTube và Facebook, đúng thứ tự đó vì mỗi bước cần link của bước trước. URL thật ghi ngược vào bảng Content.

06

Đo & báo cáo

Số liệu vào publish.json, xuất được ra HTML và Excel. Thu tự động qua API còn đang làm — nay nhập tay.

Cấu trúc thư mục

Ba tầng lồng nhau, mở ra là đọc được

Kênh chứa chiến dịch, chiến dịch chứa bài. Mỗi tầng là một thư mục thật, và mỗi tầng có đúng một file giữ sự thật của tầng đó. Không có cơ sở dữ liệu ở giữa: cái bạn nhìn thấy trong file quản lý chính là cái agent đọc.

~/.marketing — trạm nội dung
CHANNELS.md                  # sổ kênh: kênh nào nằm ở đâu
AUTHOR.md                    # tác giả là ai — dùng chung mọi kênh

<kênh>/                      # [1] MỘT KÊNH = một giọng, một tập người đọc
  channel.yml                # MÁY đọc: nền tảng, trụ nội dung, màu, đường dẫn
  brand.md                   # NGƯỜI đọc: nhận diện, giọng, chính kiến
  CAMPAIGNS.md               # sổ chiến dịch của kênh
  continuity.json            # sổ bài đã đăng — để khỏi trùng đề tài

  <chiến-dịch>/              # [2] MỘT CHIẾN DỊCH
    campaign.md              # brief + bảng Content + khối `runtime:`
    prompt.txt               # prompt bước CHỌN đề tài
    run.ps1                  # [chạy theo lịch] điểm vào duy nhất
    out/                     # sản phẩm RIÊNG của chiến dịch này
    logs/                    # nhật ký + bản chụp cấu hình mỗi lượt

    <mã>_<slug>/             # [3] MỘT BÀI
      prompt.txt             # prompt bước VIẾT bài này
      research.md            # nguồn, ràng buộc — đóng băng ở đây
      content.md             # nội dung MỌI kênh, tách theo `## post:<format>`
      publish.json           # cổng 2, URL thật, số đo
1 · KÊNH

Một giọng, một tập người đọc

Hai file chia nhau đúng một ranh giới: channel.yml là thứ máy đọc — nền tảng, trụ nội dung, bộ màu, đường dẫn. brand.md là thứ người viết và sửa — kênh này là gì, viết cho ai, không bao giờ viết gì. Trộn hai vai vào một file thì sửa câu chữ cũng phải sợ làm hỏng máy.

python scripts/pipeline/new_channel.py --id <kênh> --label "…" --path ./<kênh>
2 · CHIẾN DỊCH

Một bài toán, một mạch bài

campaign.md giữ cả brief lẫn bảng danh sách bài — mở một file là thấy chiến dịch định làm gì và đã ra được những gì. Chiến dịch chạy theo lịch (bản tin ngày, tuần) nhận thêm run.ps1 và khối runtime:; out/logs/ là của riêng nó, không dùng chung với chiến dịch khác.

python scripts/pipeline/new_campaign.py --channel <kênh> --id <slug> --name "…" --prefix XXX [--runner <script>.ps1]
3 · BÀI

Một ý tưởng, nhiều bản đăng

Một bài là một ý tưởng, nhưng ra nhiều bản đăng — blog, YouTube, Facebook — và tất cả nằm chung trong content.md, tách bằng dấu ## post:<format>. Cùng một luận điểm thì phải sửa ở một chỗ; tách ra ba file là ba bản sẽ trôi khỏi nhau sau bài thứ hai.

python scripts/pipeline/new_post.py --campaign <slug> --id XXX-001 --slug … --title "…"
KHUÔN

Cây mẫu có cùng hình dạng

templates/station/ trong repo lồng đúng như trên — _channel/_campaign/_content/. Ba lệnh ở trên chép từ đó ra, nên thứ bạn thấy trong repo chính là thứ sẽ mọc lên trong trạm. Cổng kiểm check_tree.py soi lại cây thật sau mỗi lần thêm và trả về ba trạng thái: xanh · đỏ · thiếu.

python scripts/pipeline/check_tree.py --station ~/.marketing
Cài đặt

Ba dòng lệnh và bảy câu hỏi

Clone repo rồi chạy install.ps1. Nó hỏi đúng một câu — đặt trạm nội dung ở đâu — rồi dựng khung và in ra ba lệnh tiếp theo, dán chạy được ngay. Nhấn Enter là dùng ~/.marketing. Nó không tạo kênh hộ bạn: chỗ lưu kênh là quyết định của bạn, và bước sau sẽ hỏi.

  • Windows với PowerShell 5.1 trở lên
  • Python 3.10+ kèm pyyamlopenpyxl
  • Instance có ba gốc tách rời — nội dung đặt ở kho tri thức riêng cũng được
Đọc README trên GitHub
PowerShell
git clone https://github.com/ducnguyen221/agent-marketing-studio
cd agent-marketing-studio
.\install.ps1                        # hỏi 1 câu rồi dựng khung trạm
Ranh giới rõ ràng

Repo giữ engine, bạn giữ nội dung

Một repo marketing rất dễ biến thành nơi rò rỉ: token nằm trong file config, bản nháp chưa duyệt bị commit, dữ liệu khách hàng đi theo. Ranh giới ở đây được cài bằng gitignore và mặc định an toàn, không phải bằng kỷ luật cá nhân.

Trong repo (lên GitHub) Ngoài repo (ở lại máy bạn)
Token & credential Chỉ có .env.example Mọi .env và file *token*.json đều bị gitignore
Nội dung chiến dịch Chỉ bộ mẫu KPIM mô phỏng Workbook, bản nháp, media thật của bạn
Dữ liệu & báo cáo Chỉ dữ liệu ví dụ và mô phỏng Export số liệu, lead, báo cáo xuất ra
Đăng bài Dry-run cho tới khi bạn duyệt Chỉ chạy thật khi bật autonomy: full bằng tay
Thông tin cá nhân Không PII — số điện thoại trong bộ mẫu là chuỗi sinh máy Dữ liệu khách hàng thật không bao giờ rời trạm của bạn
Mã nguồn mở

Mở repo lên, xem thử một chiến dịch hoàn chỉnh

Giấy phép MIT. Bộ mẫu KPIM cho bạn thấy đích đến trông thế nào trước khi bắt tay dựng chiến dịch của mình.