跳转至内容
  • A place to talk about whatever you want

    33 主题
    179 帖子
    semmyenatorS

    從平台策略到模型水印:AI時代下超越AGPL的開源專案保護新路徑
    隨著大型語言模型(LLM)技術的快速發展,其對現有知識產權法體系,特別是開放原始碼軟體生態系統,構成了前所未有的挑戰。其中,利用公開可見的程式碼進行模型訓練所引發的版權爭議尤為突出。在此背景下,GNU Affero 通用公共授權條款(AGPL)常被視為一種強力的防禦工具,但其效果並非萬無一失。本報告旨在深入分析除 AGPL 之外,是否存在其他有效的法律或技術替代方案,以保護開源專案的權益。研究將整合多源資訊,從法律許可證的演進、平台策略的創新、數據潔淨化的技術實踐,到模型層面的前沿防禦技術,為開源專案、企業合規及政策倡議等不同應用場景提供一套全面且具備深度的分析與建議。

    法律框架的邊界:AGPL 及其替代許可證的適用性探討
    在應對大型語言模型利用開源程式碼訓練的挑戰時,許可證作為第一道防線,其選擇與策略佈局至關重要。儘管 AGPL 因其強制性傳播原則而受到廣泛關注,但深入分析顯示,任何單一的法律工具都難以獨立解決問題,必須結合其他法律與技術手段才能形成有效防護。首先,AGPL 的核心優勢在於其「強制性傳播」特性,這一點也延伸至其前身 GNU General Public License (GPL)。根據 GPL 和 AGPL 的規定,任何基於受其保護的程式碼衍生出的作品,都必須同樣以相同的許可證發布。這一原則理論上能夠阻止商業公司將基於 GPL 或 AGPL 授權代碼訓練而成的大型語言模型直接封閉起來,從而確保其部分功能或輸出內容保持開放。然而,這種防禦的有效性正受到來自多方面的質疑與挑戰。一個關鍵問題是,即使數據集聲稱只採用了「寬鬆」許可證的代碼,實際情況卻可能截然不同。一項對六個主流 LLM 訓練數據集的分析發現,這些數據集中包含了大量源自 GPL/AGPL 代碼庫的重複檔案。這揭示了僅依賴表面許可證聲明來確保數據清白的極大漏洞,也凸顯了數據清洗過程中的巨大複雜性與潛在風險。其次,AGPL 相較於 GPL 的一個顯著優勢是其擴展的觸發條件。AGPL 不僅要求分發修改後的原始碼,還明確規定了如果使用者通過網絡服務(SaaS)的方式提供基於該模型的服務,則必須向遠端用戶提供相應的源碼。這使得 AGPL 對於以 API 形式提供的大型語言模型更具約束力,因為它能更有效地防止商業公司繞過強制性傳播的要求。然而,即便如此,AGPL 仍無法解決開放式人工智慧生態系統的根本矛盾:一旦模型權重和原始碼釋出,其後續的無限複製、部署和修改便脫離了開發者的直接控制範圍。因此,AGPL 可以說是目前法律框架下最接近理想狀態的選項,但絕非完美的答案。面對 AGPL 的侷限性,學術界和業界探索了多種替代性的法律許可證,但大多數都遭遇了同樣的困境。例如,Open Responsible AI License (OpenRAIL) 被視為一種嘗試,它在寬鬆許可的基礎上加入了類似「強制性傳播」的使用限制。然而,一篇深刻的分析指出,這種依賴道德約束和有限執行能力的使用限制性許可證,在開放生態中極易失效。文章引用了一個案例,即 GPT-J 模型被一名獨立研究員廣泛濫用,用於在線上社區生成大量有害內容,而模型發布方卻因文化因素而未採取任何行動。這個例子生動地說明,對於一個已釋出的開放模型,缺乏有效的監控和執行機制,任何基於信任的限制都將形同虛設。此外,一些新的機器學習特定許可證試圖引入額外的限制,通常涉及倫理或行為準則。然而,這些許可證的有效性同樣建立在對最終模型使用的持續監控之上,這在去中心化的開放式模型環境中幾乎是不可能實現的。因此,單純依靠創新的法律條款來解決問題,很可能只是治標不治本。有效的法律策略必須超越單一許可證的思維,轉向更宏觀的平台策略和數據治理。總結而言,在法律層面,不存在一個能完美解決所有問題的「銀彈」式許可證。有效的策略必須是多層次的組合。除了繼續使用 GPL/AGPL 來保護新創建的開源專案外,更重要的是從平台和數據層面入手,通過策略性佈局來管理風險。這包括推動平台採納更嚴格的數據共享條款,以及支援和使用經過嚴格審核的「清白」數據集,從需求側驅動整個行業合規標準的提升。

    許可證類型:核心原則,對大型語言模型訓練的適用性,主要侷限性
    GPL / AGPL:強制性傳播,衍生作品需相同許可,AGPL 將 SaaS 服務納入管制範圍,理論上可防止模型完全封閉,AGPL 對 SaaS 模型更有效,訓練數據可能含有隱蔽的強制性傳播代碼,無法控制已釋出模型的後續使用
    寬鬆許可證:允許自由使用、修改和分發,無強制性傳播要求,易於被商業公司用於訓練大型語言模型,缺乏防禦力,缺乏對模型商業化的防禦機制,使開源貢獻可能被用於盈利目的
    使用限制許可證:在許可基礎上附加特定使用限制(如禁止某些用途),理論上可用於禁止不道德的使用,執行困難,無法監控和阻止已釋出模型的違規使用
    ML 特定許可證:專門為機器學習模型設計,可能包含行為限制,尚處於探索階段,實踐效果未知,同樣面臨無法監控和執行的困境,且法律地位不明確

    平台策略與數據潔淨化:從源頭控制到構建合規數據基礎
    在法律許可證本身存在固有侷限的背景下,從平台策略和數據潔淨化兩個維度切入,成為了更為務實和根本性的防禦路徑。這兩種方法的核心思想都是從源頭控制風險,而不是在問題發生後再進行追索。GitHub 的「豁免權」條款和「Common Pile」項目分別代表了這兩種思路的成功實踐。首先,平台級的策略性佈局能夠在數據流動的初始環節就設立防線。GitHub 作為全球最大的開源代碼託管平台,在其最新的服務條款中引入了一個極具啟發性的「豁免權」條款。該條款規定,任何貢獻者向公開儲存庫提交內容時,均視為放棄了任何禁止 GitHub 或第三方(特別是指開發商用人工智慧系統的公司)透過自動化方式訪問這些公開數據的權利。這是一個主動清除版權壁壘的策略,它將數據使用的風險轉嫁到了數據的原始創作者身上,同時為 GitHub 自身及其合作夥伴進行人工智慧模型訓練掃清了法律障礙。對於開源專案而言,這一條款帶來了深刻的警示:在貢獻代碼時,必須意識到這些代碼可能被用於訓練對其所有權構成威脅的商業模型。因此,開源社群需要重新審視協作協議,考慮在其中增加關於人工智慧訓練使用的特別條款,以保障貢獻者的權益。與此相對應的另一種策略是「拒絕加入」,即主動構建一個純粹由寬鬆許可或公有領域資料組成的、經過嚴格審查的數據集。面對許多主流數據集聲稱使用的是寬鬆許可的代碼,但事實上包含了大量 GPL/AGPL 代碼的混亂局面,這種策略旨在提供一個清晰、合法、透明的替代方案。最具代表性的成功案例便是「Common Pile」項目。這個數據集完全由合法獲取、合乎倫理的內容構成,其來源經過了嚴格的法律審核,包括諮詢法律顧問和排除邊緣案例。它的誕生不僅證明了高性能量子人工智慧模型可以不用侵入式的數據採集方式訓練出來,更是對「合理使用」等辯護理由的有力回擊。Common Pile 的成功,為整個行業樹立了一個合法、合倫理的競爭對手,其戰略意義在於將舉證責任從開源社區轉移到了那些使用模糊甚至侵權數據的公司身上。無論是平台策略還是數據潔淨化,其核心都指向了對訓練數據的嚴格治理。簡單的技術手段,如基於雜湊值的比對,已被證明非常容易被繞過,只需對代碼進行微小的格式調整即可逃脫檢測。因此,真正的數據潔淨化工作必須升級為一個結合了人工審核、靜態分析、語義相似度檢測和全生命週期風險評估的綜合流程。這意味著需要投入巨大的人力和技術資源來確保數據集的合法性。科技巨頭在其企業級人工智慧解決方案中強調訓練數據的差異化與合規性,也顯示出這已經成為高端市場的重要競爭力。對於希望保護自身知識產權的開源專案和企業而言,積極參與和支援像 Common Pile 這樣經過嚴格審核的「清潔數據運動」,從需求側驅動行業標準的提升,是一條不可或缺的道路。

    技術防禦前沿:模型水印與行為對齊的應用與侷限
    當法律條文的抽象約束面臨開放式AI模型不可控的現實時,技術手段提供了更直接、更具操作性的防禦與監控路徑。在眾多技術方案中,模型水印和模型隔離與行為對齊技術正從理論走向實踐,成為彌合法律訴訟與技術現實之間差距的關鍵橋樑。模型水印技術的核心思想是在模型內部或其輸出中嵌入不易察覺的統計信號,以便未來追溯模型的來源或識別其生成內容。這為權利人和執法機構提供了關鍵的「物證」,大大增強了在侵權訴訟中的立場。其應用價值主要體現在以下幾個方面:首先,對於版權侵權偵測,權利人可以通過水印來證明某段代碼確實源自其受保護的開源專案,從而有力地反駁對方關於「獨立創作」或「巧合」的辯解。其次,對於大型語言模型發行商而言,水印技術可以用於訓練數據的過濾。發行商可以在收集網絡數據進行二次訓練時,自動過濾掉自己模型之前產生的、帶有自身水印的內容,從而避免模型陷入「複製」和知識循環的陷阱。最後,水印還可以作為一種威懾手段,讓意圖濫用模型的行為人知道其行為是可追溯的。然而,水印技術也存在不容忽視的侷限性。一方面,其檢測機制往往需要知道源模型的具體細節,這在黑箱操作的場景下很難實現。另一方面,研究人員已經證明了針對水印的「偽造攻擊」。攻擊者可以訓練自己的模型來模仿目標模型的水印特徵,從而生成看似合法但實則由惡意方創造的內容,例如偽造新聞,使得溯源變得更加複雜。這意味著水印是重要的輔助工具,而非絕對可靠的防線,但它架起了法律訴訟與技術現實之間的橋樑,是當前最有前景的技術防禦手段之一。與水印技術相輔相成的是模型隔離與行為對齊技術。雖然無法阻止模型被複製,但可以在模型設計之初就引入限制,使其輸出更符合預期。其中,基於人類反饋的強化學習(RLHF)是一種被廣泛採用的行為對齊技術。它通過讓模型學習遵循人類預設的「無害」、「忠實」等指令,來降低其被用於生成有害或侵權內容的機率。這種方法從源頭上對模型的行為進行了約束,是一種積極的防禦策略。此外,技術還可用於鑑定。一些研究致力於建立系統來區分大型語言模型生成的代碼與人類編寫的代碼,這有助於在開源項目中篩選人工智慧產出物,並評估其合規風險。同時,記憶化檢測也是技術防禦的一個重要方向。記憶化指的是模型記住了訓練數據中的特定片段並在輸出時直接複製出來。研究表明,大型語言模型存在嚴重的記憶化問題。記憶化的檢測方法已經能夠以很高的準確率判斷特定代碼是否曾出現在模型的訓練數據中。這些技術共同構成了一套從「防堵」到「溯源」再到「鑑定」的完整技術防禦體系。

    結合應用場景:為開源專案、企業合規與政策倡議提供具體指引
    綜合上述法律與技術方案的分析,可以為不同應用場景下的利益相關方提供一套具體的、可操作的策略指引。有效的防禦並非依賴單一工具,而是需要一套精心設計的組合拳,將法律、技術和社群共治深度融合。

    為開源專案制定知識產權防護策略
    對於維護開源專案的個人或組織而言,其核心目標是在促進協作的同時,最大限度地保護自身的知識產權。一個全面的防護策略應包含以下四個層面:
    採用強制性許可證:堅持為你的核心專案選擇 GPL 或 AGPL 作為基礎許可證。這是最基本的法律屏障,能夠在一定程度上阻止商業公司將你的代碼直接封閉在他們的產品中。
    審慎處理數據共享:在接受外部貢獻時,要意識到這些代碼可能被用於訓練商業大型語言模型。可以考慮在協作協議中增加關於人工智慧訓練使用的特別條款,明確告知貢獻者其代碼可能的用途。
    積極參與「清潔數據運動」:鼓勵並貢獻代碼到經過嚴格審核的數據集中。這不僅是為了推動行業標準向合法、透明的方向發展,也能間接提高你專案代碼被合規使用的機會。
    探索水印技術:如果條件允許,可以為你的專案代碼打上水印。這將為未來可能發生的侵權事件提供關鍵的技術證據,彌補法律條文在執行上的不足。

    企業在大型語言模型開發中的合規風險評估
    對於開發和使用大型語言模型的企業而言,合規風險評估是其業務的生命線。企業必須認識到,其主要風險點在於訓練數據的侵權和輸出內容的合規性。
    建立內部審計功能:領先的人工智慧開發者需要內審職能來確保合規性。企業應建立專業的內部團隊,負責對訓練數據的合法性、輸出內容的合規性進行持續審計。
    投資數據治理框架:企業必須建立一個全面的數據合規框架,對訓練數據進行全生命週期的風險評估,而不能僅憑表面許可證聲明做出判斷。這包括對數據來源的合法性進行嚴格審核,並投入資源進行數據潔淨化。
    實施輸出內容檢查:大型語言模型可能會意外複製受版權保護的文字或代碼,導致下游用戶違法。因此,企業需要開發或採購工具來檢測大型語言模型的輸出,以識別潛在的侵權內容。
    採納水印技術:在自家模型中整合水印,既是對侵權行為的威懾,也是未來保護的手段。水印技術可以幫助企業證明其模型的來源,並在發生糾紛時提供關鍵證據。

    政策或社群倡議
    為了應對大型語言模型對開源生態的挑戰,政策制定者和開源社群可以採取一系列倡議來推動系統性的變革。
    推動立法透明度:倡導立法要求人工智慧公司公開其訓練數據集的詳細資訊,包括數據來源、規模和許可證類型。這將把「舉證責任」從開發者轉移到人工智慧公司身上,迫使他們為自己的數據採集行為負責。
    重新審視「合理使用」:目前美國法院在「合理使用」原則的解釋上存在矛盾,而歐盟正在通過相關法案來規範人工智慧訓練活動。社群應持續關注並影響這些立法進程,爭取更有利於原創作者的法律框架。
    建立行業標準與認證:推動建立一個類似「合倫理人工智慧訓練」的第三方認證體系,為合規的人工智慧模型和數據集提供背書。相關成功經驗表明,這樣的藍圖具有極大的戰略價值。
    加強社群教育:提高開發者對大型語言模型訓練潛在風險的認識,鼓勵他們在貢獻代碼時做出知情選擇。這包括普及關於不同許可證的差異以及代碼可能被用於訓練人工智慧模型的資訊。

    綜上所述
    面對大型語言模型對開源生態的挑戰,需要一套組合拳,而不是指望一把萬能鑰匙。透過精心設計的許可證策略、堅決推行的數據潔淨化工程,以及積極應用前沿的水印技術,開源世界和相關利益方仍有足夠的工具來保護自身的知識產權。

  • 10 主题
    23 帖子
    SPeakS

    @dustchens 链表结构损坏, 不闭环了 (如果问题解决可以把帖子状态设置为已解决

  • 开源软件 | 开源社区 | 开源理念 | 开源与商业 | 开源可持续发展 等相关话的交流讨论
    注: 这里的"开源"是泛化的共建共享概念, 范围包含 OSI的范围、自由软件、CC等相关内容

    57 主题
    246 帖子
    MoYingJiM

    补一个 0BSD,这个许可证很多时候也被放在与 Unlicense 和 WTFPL 相提并论的(都是公共领域)

  • 64 主题
    259 帖子
    _

    分享一个我自己的一个项目中所用到的 SKILL: https://github.com/ZheFeng7110/ccc/blob/develop/.agents/skills/cpp-lib-headers-modules-both-supports/SKILL.md

    可以方便地让 Agent 创建能够同时提供头文件+模块导入的库。

    设计原理参考了 boost pfr 库中组织头文件与模块的方式。

  • 38 主题
    79 帖子
    DoomjustinD

    这是在学习了capy的隐式注入玩法后的更新。他的方案帮我梳理了我原本混乱的思路,感觉自己的目光都清撤了许多。

    旧版本的困局:stop_then 手动包装

    旧版本就是让用户手工拼装,每个 awaitable 都得用 stop_then 包一下:

    co_await stop_then(some_io_operation(), stop_token); // 得手动拼,十分繁琐

    这种操作确实实现了取消,但是所有的复杂性都暴露给了业务代码

    编写 co_await 时需要考虑"这个操作是否需要取消?" 如果需要,必须记得手动包裹 stop_then。 遗漏任何一个,就会产生"无法取消的操作",取消时挂起。

    值得一提的是,原本的实现由于混乱的思路引入了各种多线程问题。但是在梳理之后发现都是不必要处理的操作,属于是自己把自己坑进去了。

    新版本的改进:隐式注入取消 消除业务层污染

    旧方案的致命缺陷不在技术复杂度,而在于污染。业务代码里到处是 stop_then,每个 I/O 函数签名都要添加 std::stop_token 参数,这直接增加了维护成本和开发者的认知负担。

    // 旧方案:取消把整个调用链都污染了 Task<> handle_client(IOContext& ctx, std::stop_token token) { auto res = co_await stop_then(async_read(fd, buffer), token); // 得手动拼 if (!res && res.error() == std::errc::operation_canceled) { // 处理被取消 } }

    我原本认为用户可能需要自己选择可取消和不可取消,所以通过stop_then包装器可以提供最大的自由度。但是实际上,我想不出什么操作是不需要被取消的,甚至更进一步的想,一旦出现了不可取消的挂起操作,某种意义上其实可以算作一种资源泄露bug了。

    想法来源:从 capy 的实现中得到启发,我发现可以用 C++20 的 await_transform 重新设计这一层。capy 为了通用性,通过 env 注入 executor、allocator、stop_token 等参数;而我们的框架强绑定 io_uring,所以能更直接地在 promise 层注入 IOContext 和 stop_token,并统一依赖 ASYNC_CANCEL 语义。正因为这些都能确定下来,所以取消机制才能被完全自动化。

    核心改进:三层隐式注入

    第一层:Promise 环境绑定

    每个 Task 的 promise 自动持有当前的 IOContext 和 stop_token,无需用户处理:

    struct StoppablePromise { IOContext& context_; std::stop_token stop_token_; // ... };

    第二层:await_transform 统一拦截

    利用 C++20 的 await_transform 机制,框架可以在业务代码的每一个 co_await IOAwaiter 处进行拦截,自动将其升级为"取消感知"版本:

    // 新模型:零污染,框架自动处理 Task<> handle_client() { auto res = co_await async_read(fd, buffer); // 框架自动注入取消机制 if (!res && res.error() == std::errc::operation_canceled) { // 处理取消情况 } }

    第三层:统一落地语义

    StopTokenWrapper 在 awaiter 挂起期间隐式注册取消回调。被取消操作的最终结果由原操作的 completion 决定:

    也是在重新设计这块时,我发现并不需要处理 ASYNC_CANCEL 的 cqe,直接丢掉,反而让整个实现简单起来了,没有复杂的生命周期问题,也没有了被取消操作和取消操作的时序问题,被 resume 时一定是被取消操作的结果返回了,而且被取消操作的结果也是精确地,result会被正确设置。

    template<typename Promise> auto await_suspend(std::coroutine_handle<Promise> handle) noexcept -> std::coroutine_handle<> { auto inner_handle = inner_.await_suspend(handle, context()); // 核心:在挂起期隐式注册取消回调 if (promise_->stop_token.stop_possible()) stop_callback_.emplace(promise_->stop_token, CancelFn{ this }); return inner_handle; } // 取消触发时的统一路径 void CancelFn::operator()() noexcept { if constexpr (cancellable<Awaitable>) wrapper->inner_.cancel(wrapper->context()); // 若 Awaiter 实现 cancel,直接调用 else wrapper->context().cancel(wrapper->inner_); // 否则走 ASYNC_CANCEL } 修改后

    隐式注入:用户编写普通的 co_await,框架在后台自动升级为"取消感知"版本。

    生命周期简化:由于 cancel CQE 直接扔掉,不再跟踪,stop_callback 的生命周期严格绑定在 awaiter 挂起这段期间。不需要 shared_ptr<bool> 守卫了。

    awaiter 契约统一

    要是 awaiter 实现了 cancel(io_context) 方法,框架就调它。 否则用默认的 io_uring ASYNC_CANCEL 方式。 核心代码对比

    旧版本(用户手动拼):

    auto task = [](std::stop_token token) -> async::Task<> { co_await stop_then(some_io(), token); // 得手动传 token,手动调用 stop_then }; co_await async::any(task(group.stop_token()), ...);

    新版本(框架自动处理):

    auto task = []() -> async::Task<> { co_await some_io(); // 啥都不用干,框架已经全搞定 }; // stop_token 框架内部在 Task::promise 里已经绑了,用户看不见 多线程下的行为对比 旧版本 主线程 (IOContext.run()) ↓ [处理 I/O 完成事件A] ↓ task 协程被唤醒 ↓ stop_then 析构(alive_ = false) ↓ 后台定时器线程 ↓ [发送 stop_token cancel] ↓ post 任务进入队列 ↓ [IOContext 取出 post 任务,检查 alive_] ↓ *alive_ == false,安全丢弃

    陷阱:如果 post 任务执行前,协程没析构,就访问了已 free 的内存。

    新版本 主线程 (IOContext.run()) ↓ task 协程开始 co_await some_io() ↓ await_transform 自动把它包装成 StopTokenWrapper ↓ stop_callback 被注册在 wrapper 的栈帧上 ↓ 后台定时器线程 ↓ [stop_token 被触发] ↓ stop_callback 立刻在这个线程上执行(标准库保证线程安全) ↓ 调用 awaiter.cancel(context) 把 cancel 指令 post 回主线程 ↓ 主线程 (IOContext.run()) ↓ [从事件循环取出 cancel 指令] ↓ 发送 ASYNC_CANCEL SQE 给内核 ↓ 原操作的 CQE 返回 -ECANCELED 或原结果 ↓ awaiter 恢复协程,返回错误给上层

    如此一来:

    stop_callback 本身线程安全(标准库保证)。 cancel 动作在 IOContext 里执行,安全、可控。 栈帧生命周期自动保护,不需要 shared_ptr 守卫。 结尾

    由于这个修改算是撬了最底层的设计,所以导致绝大部分的模块都需要重写。不过在理清了混乱之后,所有的实现相较于旧版本都变得简单了很多,甚至绝大部分的Awaiter实现都变成了下面这种极简版本,也就是继承一下IOAwaiter,写个 prepare()和构造函数就完事了。

    class SendAwaiter : public async::IOAwaiter<SendAwaiter, std::size_t> { private: int fd_; std::span<const std::byte> buffer_; public: SendAwaiter(int fd, std::span<const std::byte> buffer) : fd_{ fd } , buffer_{ buffer } {} void prepare(::io_uring_sqe* sqe) const noexcept { ::io_uring_prep_send(sqe, fd_, buffer_.data(), buffer_.size(), MSG_NOSIGNAL); } };
  • 一个技术知识分享、学习、交流的社区

    16 主题
    54 帖子
    sunrisepeakS

    @dustchens 已支持

  • Got a question? Ask away!

    4 主题
    14 帖子
    SPeakS

    备注一下使数学公式的使用语法

    单行公式语法 - $ 你的公式 $

    $ log_2^n $

    $ log_2^n $

    多行公式语法 - $$ 你的公式 $$

    $$ log_2^n => log_2^9 = 3 , n = 9 $$

    $$
    log_2^n =>
    log_2^9 = 3, n = 9
    $$

公告栏 | Bulletin Board

欢迎加入d2learn社区 - 社区指南
Welcome to the d2learn Community - Community Guide

一个以 [知识、技术、代码、项目、想法、开源] 相关话题为主导的社区
A community focused on topics related to [knowledge, technology, code, projects, ideas, and open source].


在线用户