iT邦幫忙

2025 iThome 鐵人賽

DAY 26
1
Odoo

Odoo × 生成式 AI:從零到一的企業自動化實戰系列 第 26

Day 26:人機協作新典範:Human-in-the-Loop 流程設計

  • 分享至 

  • xImage
  •  

你將學到

  • Human-in-the-Loop (HITL) 的概念與在 AI + ERP 流程中的重要性,如何平衡 AI 建議與人類審核的機制。
  • Odoo 中設計 AI + 人類審核的協作架構。

關鍵字

HITL、人機協作、審核記錄


情境

Odoo ERP 系統新部署了 AI 功能,業務人員按一下按鈕,就讓 AI 根據機器學習模型自動產生了一份合約草稿。聽起來很方便,但您會毫不猶豫地直接將這份草稿發給客戶嗎?

又或者,客服模組引入了 GPT 助理,收到客戶抱怨時 AI 已經自動擬好了一封道歉信給客戶——您會讓 AI 直接發信嗎?

事實上,AI 再聰明,也需要人類的適時把關。這正是我們今天要討論的 Human-in-the-Loop (人機協作) 流程設計。AI 提供初稿或建議的同時,我們插入必要的人類審核節點,以確保效率精準度兼顧。

在 AI 與 ERP 系統結合的浪潮下,人機協作正成為新典範。透過 HITL 流程,我們可以結合機器的速度與人類的專業判斷,達成1+1>2的效果。

換言之,我們應該把 AI 看做一位資淺助理——能替我們起草內容、提出建議,但最後的定奪仍需有經驗的主管(人類)審閱。透過適當的 HITL 策略,AI 的效率與人類的判斷力可以在關鍵決策上取得平衡。


Human-in-the-Loop:AI + ERP 的協作意義

在傳統 ERP 流程中,自動化追求的是減少人工操作、提高處理速度。然而在引入 AI 後,我們除了自動化,更面臨「自動化的結果是否可信?」的新課題**。**

HITL 的核心理念,就是在自動化流程的關鍵點引入人工確認,以結合自動化的效率人工的可靠度。

具體來說,我們會在流程中設計審核檢查點,確保 AI 的輸出經過人類專家把關後再向下游傳遞。這種機制在 AI + ERP 情境下有多項好處:

  • 降低風險,提升準確度:AI 可能產生「幻覺」(虛構錯誤資訊)或不合規內容。在人機協作流程中,人工審核能及時過濾這些錯誤,避免自動化誤傷。
  • 符合法規與合約要求:許多領域(例如財務報表、法律合約)有嚴格的合規要求,必須有人工簽核紀錄。引入 HITL 流程,確保即便使用 AI,自動化結果也符合內部控制和法規需要。
  • 增進使用者信任:讓最終用戶知道 AI 不是黑箱獨裁者,而是輔助者。使用者始終保有對結果的最終決定權,有助於建立對 AI 功能的信心。當員工發現 AI 建議僅作為選項而非強制,他們更願意採納 AI 輔助提升生產力。
  • 持續改進:透過在人機協作中收集人工的反饋資料(例如哪些 AI 建議被經常修改/拒絕),我們可以迭代優化 AI 的提示詞 (Prompt) 和模型,讓 AI 在未來更貼近人類期望。每一次人工調整,其實都是在教AI下次做得更好。

在 ERP 各模組中,其實早已有許多適合 HITL 的應用場景。我們接下來將簡述幾個 Odoo 中的 AI 應用案例,看看哪些情境適合全自動處理,哪些需要人類介入,從而體會 HITL 設計的重要性。


案例一:CRM 自動化 vs 人工確認

Odoo 18 引入 AI 之前,業務單位常需要手動整理潛在客戶資訊、標記客戶產業、評估商機分數等。現在借助 AI,我們可以自動化許多 CRM 任務。例如:

  • 潛在客戶產業自動標記:AI 模型可根據公司名稱或描述,自動將 leads 歸類為「軟體業」「零售業」等類別。這類任務屬於低風險的資料標註,即使有點誤差也無傷大雅,因此完全自動化無須人為審核。AI 每天替銷售代表節省大量瑣碎整理的時間。
  • 商機成功率預測:透過歷史數據訓練的模型自動給出每個機會的預測得分,銷售經理可以據此優先跟進高分機會。然而這類 AI 建議通常作為參考資訊,決策仍由經理依經驗判斷。有些公司會在系統中記錄 AI 給分和最後實際結果,以追蹤模型準確度並不斷調整演算法。
  • 郵件回覆助手:當 CRM 收到客戶郵件詢問時,Odoo 18 的 AI 可以根據對話內容自動擬定回覆草稿。這封草稿會出現在業務人員的郵件編輯介面中,供他們審閱、編輯後再正式發送。AI 幫你寫好了 80%,但最後 20% 的語氣調整與確認仍由人來完成,以確保回覆貼合公司風格與正確性。這就是典型的 HITL:AI 提供初稿,人類掌握決定

上述案例中,我們看到 AI 自動化人工確認之間要視情境拿捏:對低影響、標準化的任務(如標籤分類),AI 可以直接自動完成;但對高風險、高語意要求的任務(如客戶溝通內容),則需要人員在 loop 中核查。


案例二:AI 協助合約草擬與法務審核

某公司引入 Odoo 的 Contracts 模組管理各類合約,並希望利用 AI 自動產生合約初稿。典型流程如下:

  1. AI 產生合約草稿 – 使用者在 Odoo 中輸入關鍵資訊(例如合約類型、當事人、重要條款要點等),點擊「AI 產生草稿」。系統透過 OpenAI API 或內建 LLM 模型,根據預設的 Prompt 生成一份合約內容文字。這份草稿會寫入合約記錄的內容欄位,同時合約狀態標記為「待審核」。
  2. 法務人員審查 – 負責審核的法務專員在 Odoo 中打開這份草稿。此時他可以看到 AI 生成的合約內容,以及相關背景(例如客戶資訊、合約模板版本、AI 提示詞要求)。法務專員有三種審查決策可選:
    • 接受:對 AI 草稿非常滿意,無需任何修改即可採納。
    • 修改:發現草稿有部分措辭或條款需調整,直接在系統中編輯修改草稿內容,使之符合要求。
    • 退回:認為草稿偏離需求太遠,無法修正,決定退回重寫。可能選擇由人手工起草,或要求 AI 重新產生新的版本。
  3. 記錄審核決策 – 不論法務選擇哪種決策,系統都需要將此審核結果記錄下來:包括審核人、時間、動作(接受/修改/退回)、以及相關的批註或修改內容。這份記錄將作為合約的審核日誌保存,以供日後查詢和審計。例如可以在合約的 Chatter 留言中自動留下「法務 A 於 2025-10-10 接受 AI 草稿 (v1) 作為最終合約」的訊息,或「法務 B 修改草稿並採納 (v2)」。必要時,還可將不同版本的草稿存檔備查。
  4. 定稿與歸檔 – 如果草稿被接受或經修改後接受,則合約狀態轉為「已定稿/生效」,進入後續簽署流程。若被退回,則流程可能迴圈:根據退回意見產生新草稿再次審核,直到定稿。所有迴圈過程都會版本化追蹤,確保每一次改動有跡可循。

上述流程以 Human-in-the-Loop 模式運作:AI 的角色是助手,提供合約初稿;人類的角色是監督者,最終拍板合約內容是否可用。

odoo-contract-hitl-flow

如上圖所示,流程在 法務審查 節點根據人工決策而分支:接受或修改的結果都進入定稿階段並做好紀錄;若退回則記錄原因,並可能重新啟動草擬流程(圖中簡化表示退回即結束此次流程,但實務中通常會進入下一個草稿版本循環)。

💡 Gary’s Pro Tip|審核流程效率優化
在設計 HITL 流程時,要盡量提高人工審核的效率。提供充足的背景與對比能幫助審核人員快速做決定:例如在審查介面上,同時顯示AI 提示詞內容、引用的模板條款,甚至標註 AI 修改過的部分。這樣法務可以一眼看出 AI 草稿與標準條款的差異,重點審核異動處即可,大大節省時間。


在 Odoo 中實作 HITL 協作流程

了解流程概念後,我們進入技術層面,看看如何在 Odoo 中落地實現上述 AI + 人審協作。

架構設計與模型準備

首先,在資料模型上我們需要能夠儲存多版本草稿審核記錄。一種做法是為合約模型新增一個 One2many 關聯到 contract.review.log 模型,用來記錄每次 AI 產生或人工審核的結果。

contract.review.log 可以包含欄位:合約參照、版本號、草稿內容、審核者、動作、時間戳記、備註等。如此一來,每次生成新草稿或審核定稿,都新增一筆 log,做到完整的版本追蹤。例如:

class ContractReviewLog(models.Model):
    _name = 'contract.review.log'
    _description = 'Contract Review Log'
    contract_id = fields.Many2one('my.contract', required=True)
    version = fields.Integer(string='版本')
    draft_text = fields.Text(string='草稿內容')
    reviewed_text = fields.Text(string='審核後內容')
    action = fields.Selection([('accept','接受'),('modify','修改'),('reject','退回')], string='動作')
    reviewer_id = fields.Many2one('res.users', string='審核人')
    review_date = fields.Datetime(default=fields.Datetime.now, string='審核時間')
    notes = fields.Text(string='備註意見')

主合約模型(my.contract)則需要關聯這些日誌,並控制狀態流轉:

  • content 欄位:文本欄位存放當前合約內容(最新版本的草稿或定稿)。
  • state 欄位:例如 'draft'(草稿待產生), 'pending_review'(待審核), 'approved'(定稿完成), 'rejected'(已退回) 等。
  • current_version 欄位:紀錄目前草稿版本號。
  • review_log_ids: One2many 至 contract.review.log,方便在合約表單裡顯示歷次記錄。

接著是AI 生成草稿提交審核的邏輯。我們可以在合約模型上定義伺服器動作 (Model method),如下:

class Contract(models.Model):
    _name = 'my.contract'
    _inherit = ['mail.thread']  # 使其擁有 Chatter 討論串
    content = fields.Text('合約內容')
    state = fields.Selection([...], default='draft')
    current_version = fields.Integer('版本', default=0)
    review_log_ids = fields.One2many('contract.review.log', 'contract_id')

    def action_generate_ai_draft(self):
        """調用 AI 服務產生合約草稿"""
        prompt = self._prepare_contract_prompt()  # 準備提示詞,組合合約要點
        ai_text = call_external_ai(prompt)        # 呼叫外部 AI(如 OpenAI API)取得草稿文本
        self.write({
            'content': ai_text,
            'state': 'pending_review',
            'current_version': self.current_version + 1
        })
        # 建立日誌紀錄
        self.env['contract.review.log'].create({
            'contract_id': self.id,
            'version': self.current_version,
            'draft_text': ai_text,
            'action': 'draft'
        })
        # 在 Chatter 留言
        self.message_post(body=f"AI 產生了合約草稿 v{self.current_version},等待審核。")
        return True

    def action_approve(self):
        """法務接受草稿"""
        self.write({'state': 'approved'})
        self.env['contract.review.log'].create({
            'contract_id': self.id,
            'version': self.current_version,
            'reviewed_text': self.content,
            'action': 'accept',
            'reviewer_id': self.env.uid
        })
        self.message_post(body=f"合約草稿 v{self.current_version} 已由 {self.env.user.name} 批准定稿。")
        return True

    def action_mark_need_modify(self, edited_text):
        """法務修改後接受草稿"""
        old_text = self.content
        # 將內容更新為法務修改後的版本
        self.write({
            'content': edited_text,
            'state': 'approved',
            'current_version': self.current_version + 1
        })
        # 紀錄修改前後內容差異
        self.env['contract.review.log'].create({
            'contract_id': self.id,
            'version': self.current_version,
            'draft_text': old_text,
            'reviewed_text': edited_text,
            'action': 'modify',
            'reviewer_id': self.env.uid
        })
        self.message_post(body=f"合約草稿經 {self.env.user.name} 修改後定稿 (v{self.current_version})。")
        return True

    def action_reject(self, reason):
        """法務退回草稿"""
        self.write({'state': 'rejected'})
        self.env['contract.review.log'].create({
            'contract_id': self.id,
            'version': self.current_version,
            'action': 'reject',
            'reviewer_id': self.env.uid,
            'notes': reason or ''
        })
        self.message_post(body=f"草稿 v{self.current_version} 被 {self.env.user.name} 退回。")
        return True

上面的程式碼展示了關鍵步驟:產生草稿時將內容寫入並狀態設為待審核、審核通過或修改則將狀態轉為已定稿、退回則標記為已退回。

同時每個步驟都在 contract.review.log 中新增紀錄,並使用 message_post 在合約的聊天紀錄裡留下可讀性好的訊息。由於我們讓合約模型繼承了 mail.thread,使用者在前端就能看到這些審核訊息串。

以上程式碼為簡化示意,實務上可能需要考慮更多狀態轉換限制、錯誤處理(如 AI 調用失敗時的例外)等等。重點是要有:模型儲存版本、方法處理狀態與紀錄、Chatter 保留歷程


前端 UI:呈現草稿與審核操作

接下來考慮前端使用者介面。我們可以透過 Odoo 視圖(Views)來設計友善的 HITL 介面。在合約的表單視圖中,要讓使用者方便地進行審核操作並檢視歷史版本:

  • 草稿內容區塊:顯示當前草稿內容(可使用 <field name="content" widget="html"/> 或文本區域,讓法務直接編輯)。如果狀態是 pending_review,內容欄位可以是編輯模式;一旦定稿,內容欄位設為唯讀防止再更改(除非啟動新版本流程)。
  • 審核動作按鈕:根據合約當前狀態,在表單頂部顯示對應的按鈕。例如狀態為 pending_review 時,顯示「接受」「需要修改」「退回」三個按鈕;狀態為 draft 時顯示「AI 產生草稿」按鈕;狀態為 rejected 時可再次產生草稿或改人工起草等。這些按鈕透過 attrs 控制隱藏/顯示,點擊後調用前述伺服器方法。
  • 版本切換/比較:為方便法務對照不同版本,我們可以在表單中加入一個下拉選單或清單,列出歷史版本號。選擇某版本時,彈出對話框或側邊欄顯示該版本的內容,甚至可以對比當前版本差異。如果要高亮差異,可以考慮在後端生成diff結果,例如將兩版本文字進行比對,標出新增刪除部分,再以 HTML 呈現。例如使用 Python 的 difflib 將差異轉成帶標記的 HTML,儲存在某個欄位,再透過 QWeb 在前端渲染。或者更簡單地,提供一個「顯示變更」按鈕,點擊後使用 JavaScript 將兩文本進行逐字對比,高亮出不同之處。以下是一個基本的合約表單視圖片段範例:
<record id="view_contract_form" model="ir.ui.view">
    <field name="model">my.contract</field>
    <field name="arch" type="xml">
        <form string="Contract">
            <header>
                <button name="action_generate_ai_draft" string="AI 產生草稿" type="object"
                        attrs="{'invisible': [('state', '!=', 'draft')]}"/>
                <button name="action_approve" string="接受草稿" type="object"
                        attrs="{'invisible': [('state', '!=', 'pending_review')]}"/>
                <button name="%(action_contract_mark_modify)d" string="需要修改" type="action"
                        attrs="{'invisible': [('state', '!=', 'pending_review')]}"/>
                <button name="action_reject" string="退回草稿" type="object"
                        attrs="{'invisible': [('state', '!=', 'pending_review')]}"/>
                <field name="state" widget="statusbar" statusbar_visible="draft,pending_review,approved,rejected"/>
            </header>
            <sheet>
                <group>
                    <field name="name" placeholder="合約名稱"/>
                    <field name="partner_id" placeholder="相對方公司"/>
                    <field name="current_version" readonly="1" invisible="1"/>
                </group>
                <group string="合約內容">
                    <field name="content" widget="text" nolabel="1"
                           attrs="{'readonly': [('state', '=', 'approved')]}"/>
                </group>
                <group string="歷史版本">
                    <field name="review_log_ids" context="{'contract_id': id}" mode="tree"
                           colspan="2" readonly="1">
                        <tree>
                            <field name="version"/>
                            <field name="review_date"/>
                            <field name="reviewer_id"/>
                            <field name="action"/>
                            <field name="notes"/>
                        </tree>
                    </field>
                </group>
            </sheet>
            <div class="oe_chatter">
                <field name="message_ids" widget="mail_thread"/>
            </div>
        </form>
    </field>
</record>

上述視圖重點有:根據 state 顯示不同按鈕;利用 statusbar 顯示流程階段;內容欄位在定稿後唯讀;下方一個只讀的歷史版本清單,列出每次審核的動作和備註。

使用者可以點擊某條歷史紀錄(例如 v1 被退回)查看詳細資訊或內容對比(進階做法可在 contract.review.log 的表單視圖中呈現當時的草稿全文)。

💡 Gary’s Pro Tip|完善審核日誌與版本管理
務必完整紀錄每一次 AI 建議與人類決策:包括 AI 用了什麼提示詞、生成了哪些內容、人類為何做出修改或退回。這些審核日誌不僅是追溯責任的依據,更是分析 AI 表現寶貴的資料。如果發現某類合約 AI 草稿經常被退回,我們可以據此調整 Prompt 編寫或模型參數;如果某段文本每次都被法務修改,我們能將該修改意見匯入 AI 的知識庫或模板,下次自動套用。良好的版本管理與日誌讓 HITL 流程形成閉環反饋,AI 和人都在這循環中持續進步。


今日結語

透過今天的介紹,我們在 Odoo 中建構了一套 「AI 生產初稿、人類審核定稿」 的協作機制。

這種 Human-in-the-Loop 流程帶來的,不僅是即時的品質管控,更是在組織內建立一種人機信任的新文化:AI 不再是讓人不安的黑盒,而是可靠的助手,因為每一步都有人類監督。

長遠來看,隨著日誌中累積大量審核資料,我們可以分析哪些環節耗時、哪些類型錯誤頻發,進而優化流程瓶頸。例如,也許 AI 在某些合約類型表現特別好,未來可考慮逐步放寬該類型的人工介入程度;反之,對某些高風險場景則提高人工覆核比例,形成動態協作策略

HITL 並不是讓自動化退步,反而是讓自動化更聰明。透過「人機協作」策略,我們把 AI 的優勢最大化,同時避免其缺點釀成錯誤。

AI 的導入不是為了取代人類判斷,而是為了強化人類決策

在 Odoo 18 的生態中,我們已經看見許多這樣的成功案例,從 CRM 客戶建檔到 Helpdesk 回覆,再到合約審閱,都能找到 AI 與人協同的蹤跡。希望今天的分享,讓大家在實務導入 AI 時,能妥善設計 Human-in-the-Loop 流程,打造出效率與精準並存的新一代人機協作體驗!


上一篇
Day 25: 極致用戶體驗:AI 友善的 UI/UX 設計
下一篇
Day 27:專案實戰藍圖:AI 導入 Odoo 的完整實施指南
系列文
Odoo × 生成式 AI:從零到一的企業自動化實戰30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言