Tự động hóa tài liệu kỹ thuật với LLM và GitHub Actions

AI tutorial - IT technology blog
AI tutorial - IT technology blog

Khủng hoảng tài liệu lúc 2 giờ sáng

Vào lúc 2 giờ sáng một ngày thứ Ba, bản hotfix production cuối cùng cũng được triển khai. Chúng tôi vừa refactor một module xác thực quan trọng, thay đổi ba API endpoint chính để xử lý lỗi race condition. Code chạy hoàn hảo. Tuy nhiên, đến 9 giờ sáng, tôi nhận được 14 tin nhắn Slack chưa đọc từ team frontend. Các lời phàn nàn đều giống nhau: ‘API trả về lỗi 404’, ‘README vẫn hiển thị auth header cũ’, và ‘Changelog cho bản release này đâu rồi?’

Tôi nhận ra rằng trong khi pipeline CI/CD cho code của chúng tôi đã rất hiện đại, thì việc làm tài liệu về cơ bản vẫn là thủ công. Chúng tôi dựa vào trí nhớ của lập trình viên để cập nhật các file Markdown. Trong những đợt triển khai đầy áp lực, tài liệu luôn là “nạn nhân” đầu tiên bị bỏ quên. Tình trạng ‘tài liệu mục nát’ (documentation rot) này không chỉ gây khó chịu cho đồng nghiệp mà còn tạo ra các lỗi tích hợp và làm chậm toàn bộ quy trình kỹ thuật. Việc sử dụng CLAUDE.md để lập trình với AI thông minh hơn là một cách tiếp cận tương tự để quản lý tri thức trong dự án.

Nguyên nhân gốc rễ: Tại sao tài liệu của bạn luôn lỗi thời

Sau sự cố đó, tôi đã phân tích lý do tại sao quy trình của mình thất bại. Vấn đề không phải do sự lười biếng, mà là do những rào cản cố hữu trong quy trình làm việc truyền thống:

  • Gánh nặng nhận thức (Cognitive Load): Sau khi hoàn thành một tính năng phức tạp, lập trình viên rất khó để chuyển từ ‘chế độ logic’ sang ‘chế độ viết tài liệu kỹ thuật’.
  • Đồng bộ hóa thủ công: Việc thay đổi một signature của hàm trong auth.py yêu cầu phải tìm và thay thế thủ công trong README.mdAPI_DOCS.md.
  • Thiếu bước kiểm chứng (Validation): Pre-commit hook có thể kiểm tra lỗi cú pháp, nhưng chúng không thể xác minh liệu một đoạn văn tiếng Anh có phản ánh chính xác thay đổi logic trong Go hay Python hay không.

Các công cụ như JSDoc hay Doxygen có giúp ích đôi chút, nhưng chúng thường tạo ra nội dung khô khan và máy móc. Chúng bỏ lỡ cái ‘tại sao’ đằng sau một thay đổi. Hơn nữa, chúng yêu cầu một kỷ luật viết comment mà hầu hết các team đang chạy nhanh trong sprint đơn giản là không thể duy trì được.

So sánh các phương pháp: Thủ công vs Truyền thống vs LLM

Trước khi xây dựng một giải pháp tùy chỉnh, tôi đã đánh giá ba cách chính để xử lý tài liệu kỹ thuật:

  1. Cập nhật thủ công: Chính xác nếu được thực hiện đúng, nhưng độ tin cậy bằng không và rào cản thực hiện rất cao.
  2. Trình tạo tĩnh (Swagger/Doxygen): Đáng tin cậy về mặt cấu trúc. Tuy nhiên, chúng gặp khó khăn với các giải thích cấp cao và dù sao cũng yêu cầu lập trình viên phải viết tài liệu ngay trong code.
  3. Tự động hóa bằng LLM: Sử dụng các mô hình ngôn ngữ lớn (LLM) để phân tích code diff và tạo ra tài liệu mà con người có thể đọc được. Nó nắm bắt được ý định của thay đổi, chứ không chỉ là cú pháp.

Lựa chọn đã rõ ràng. Bằng cách tích hợp LLM vào workflow của GitHub, chúng tôi có thể tự động hóa các phần tẻ nhạt của việc làm tài liệu trong khi vẫn duy trì nội dung chất lượng cao và dễ đọc.

Giải pháp: Pipeline GitHub Actions + LLM

Chiến lược hiệu quả nhất là kích hoạt đồng bộ hóa tài liệu mỗi khi một Pull Request được merge vào nhánh chính. Đây là một bước quan trọng trong việc xây dựng hạ tầng tự phục hồi và tự động hóa quy trình kỹ thuật. Pipeline tuân theo một logic đơn giản: phát hiện thay đổi, trích xuất ngữ cảnh, gửi prompt cho LLM, cập nhật file và commit các thay đổi.

Bước 1: Thiết lập GitHub Action

Chúng ta cần một workflow kích hoạt khi có push vào nhánh main. Workflow này yêu cầu quyền truy cập repository và một API key cho LLM bạn chọn (OpenAI, Claude, hoặc Gemini).

name: Auto-Doc Generator

on:
  push:
    branches:
      - main

jobs:
  update-docs:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout mã nguồn
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Cài đặt Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.10'

      - name: Cài đặt thư viện
        run: |
          pip install openai gitpython

      - name: Chạy script tạo tài liệu
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: python scripts/generate_docs.py

      - name: Commit và Push các thay đổi
        run: |
          git config --global user.name 'github-actions[bot]'
          git config --global user.email 'github-actions[bot]@users.noreply.github.com'
          git add README.md API_DOCS.md CHANGELOG.md
          git commit -m "docs: cập nhật tự động qua pipeline LLM [skip ci]" || echo "Không có thay đổi nào để commit"
          git push

Bước 2: Script logic cốt lõi

Script Python này đóng vai trò là động cơ chính. Nó xác định các thay đổi trong commit cuối cùng và hướng dẫn LLM cập nhật các file liên quan. Sử dụng GitPython, chúng ta có thể tách biệt chính xác phần diff giữa commit hiện tại và commit trước đó.

import os
from openai import OpenAI
from git import Repo

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

def get_code_diff():
    repo = Repo(".")
    diff = repo.git.diff('HEAD~1', 'HEAD')
    return diff

def update_file(filename, prompt_context):
    with open(filename, "r") as f:
        current_content = f.read()

    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": f"Bạn là một người viết tài liệu kỹ thuật. Hãy cập nhật file {filename} dựa trên các thay đổi của code. Giữ nguyên văn phong và định dạng hiện có."},
            {"role": "user", "content": f"Nội dung {filename} hiện tại:\n{current_content}\n\nThay đổi của Code:\n{prompt_context}"}
        ]
    )
    return response.choices[0].message.content

def main():
    diff = get_code_diff()
    if not diff:
        return

    # Cập nhật README và Changelog
    for doc_file in ["README.md", "CHANGELOG.md"]:
        updated_content = update_file(doc_file, diff)
        with open(doc_file, "w") as f:
            f.write(updated_content)

if __name__ == "__main__":
    main()

Bước 3: Thiết kế Prompt đáng tin cậy

Chất lượng tài liệu phụ thuộc hoàn toàn vào prompt của bạn. Nếu bạn chỉ yêu cầu LLM ‘cập nhật tài liệu’, nó có thể bị ảo giác hoặc xóa các phần quan trọng. Sự cụ thể là yếu tố sống còn. Đối với CHANGELOG.md, tôi sử dụng một prompt bắt buộc tuân theo tiêu chuẩn ‘Keep a Changelog’ (Added, Changed, Deprecated, Removed, Fixed).

Cung cấp nội dung file hiện có cho LLM là một bước không thể thương lượng. Ngữ cảnh này giúp mô hình duy trì văn phong và định dạng nhất quán, tương tự như cách chúng ta xây dựng AI Pipeline mạnh mẽ với đầu ra có cấu trúc. Nếu không có nó, tài liệu của bạn cuối cùng sẽ trông giống như một mảnh vá rời rạc của nhiều phong cách viết khác nhau.

Xử lý các trường hợp biên và độ tin cậy

Các lần triển khai ban đầu thường gặp hai rào cản. Thứ nhất, các bản diff lớn có thể vượt quá giới hạn 128k token của các mô hình như GPT-4o. Để giải quyết vấn đề này, tôi đã sửa đổi script để lọc bỏ các file không liên quan, chẳng hạn như .css, .svg hoặc lockfile, trước khi gửi dữ liệu đến API.

Thứ hai, bạn phải tránh ‘Vòng lặp vô tận’ (Infinite Loop). Vì GitHub Action commit các thay đổi ngược lại repository, về lý thuyết nó có thể tự kích hoạt chính nó mãi mãi. Thêm [skip ci] vào commit message là cách tiêu chuẩn trong ngành để ngăn chặn sự đệ quy này.

Kết quả: Tài liệu tiến hóa cùng mã nguồn

Kể từ khi chuyển sang pipeline tự động này, những cơn hoảng loạn trên Slack lúc 9 giờ sáng đã biến mất. Khi một lập trình viên merge một PR, tài liệu sẽ được cập nhật trong vòng khoảng 90 giây. Độ chính xác cao đến ngạc nhiên. Điều này là do LLM phân tích việc triển khai thực tế thay vì dựa vào những gì lập trình viên định viết.

Tài liệu không còn là một công việc nhàm chán mà chúng tôi khiếp sợ vào cuối mỗi sprint. Nó đã trở thành một sản phẩm phụ tự nhiên trong quy trình phát triển của chúng tôi. Nếu bạn đã mệt mỏi với việc tài liệu bị sai lệch, việc giao phó những phần nặng nhọc cho LLM là một giải pháp thực tế và có tác động lớn cho bất kỳ đội ngũ kỹ thuật nào.

Share: