HITL、人機協作、審核記錄
Odoo ERP 系統新部署了 AI 功能,業務人員按一下按鈕,就讓 AI 根據機器學習模型自動產生了一份合約草稿。聽起來很方便,但您會毫不猶豫地直接將這份草稿發給客戶嗎?
又或者,客服模組引入了 GPT 助理,收到客戶抱怨時 AI 已經自動擬好了一封道歉信給客戶——您會讓 AI 直接發信嗎?
事實上,AI 再聰明,也需要人類的適時把關。這正是我們今天要討論的 Human-in-the-Loop (人機協作) 流程設計。AI 提供初稿或建議的同時,我們插入必要的人類審核節點,以確保效率與精準度兼顧。
在 AI 與 ERP 系統結合的浪潮下,人機協作正成為新典範。透過 HITL 流程,我們可以結合機器的速度與人類的專業判斷,達成1+1>2的效果。
換言之,我們應該把 AI 看做一位資淺助理——能替我們起草內容、提出建議,但最後的定奪仍需有經驗的主管(人類)審閱。透過適當的 HITL 策略,AI 的效率與人類的判斷力可以在關鍵決策上取得平衡。
在傳統 ERP 流程中,自動化追求的是減少人工操作、提高處理速度。然而在引入 AI 後,我們除了自動化,更面臨「自動化的結果是否可信?」的新課題**。**
HITL 的核心理念,就是在自動化流程的關鍵點引入人工確認,以結合自動化的效率和人工的可靠度。
具體來說,我們會在流程中設計審核檢查點,確保 AI 的輸出經過人類專家把關後再向下游傳遞。這種機制在 AI + ERP 情境下有多項好處:
在 ERP 各模組中,其實早已有許多適合 HITL 的應用場景。我們接下來將簡述幾個 Odoo 中的 AI 應用案例,看看哪些情境適合全自動處理,哪些需要人類介入,從而體會 HITL 設計的重要性。
在 Odoo 18 引入 AI 之前,業務單位常需要手動整理潛在客戶資訊、標記客戶產業、評估商機分數等。現在借助 AI,我們可以自動化許多 CRM 任務。例如:
上述案例中,我們看到 AI 自動化與人工確認之間要視情境拿捏:對低影響、標準化的任務(如標籤分類),AI 可以直接自動完成;但對高風險、高語意要求的任務(如客戶溝通內容),則需要人員在 loop 中核查。
某公司引入 Odoo 的 Contracts 模組管理各類合約,並希望利用 AI 自動產生合約初稿。典型流程如下:
上述流程以 Human-in-the-Loop 模式運作:AI 的角色是助手,提供合約初稿;人類的角色是監督者,最終拍板合約內容是否可用。
如上圖所示,流程在 法務審查 節點根據人工決策而分支:接受或修改的結果都進入定稿階段並做好紀錄;若退回則記錄原因,並可能重新啟動草擬流程(圖中簡化表示退回即結束此次流程,但實務中通常會進入下一個草稿版本循環)。
💡 Gary’s Pro Tip|審核流程效率優化
在設計 HITL 流程時,要盡量提高人工審核的效率。提供充足的背景與對比能幫助審核人員快速做決定:例如在審查介面上,同時顯示AI 提示詞內容、引用的模板條款,甚至標註 AI 修改過的部分。這樣法務可以一眼看出 AI 草稿與標準條款的差異,重點審核異動處即可,大大節省時間。
了解流程概念後,我們進入技術層面,看看如何在 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 保留歷程。
接下來考慮前端使用者介面。我們可以透過 Odoo 視圖(Views)來設計友善的 HITL 介面。在合約的表單視圖中,要讓使用者方便地進行審核操作並檢視歷史版本:
<field name="content" widget="html"/>
或文本區域,讓法務直接編輯)。如果狀態是 pending_review,內容欄位可以是編輯模式;一旦定稿,內容欄位設為唯讀防止再更改(除非啟動新版本流程)。attrs
控制隱藏/顯示,點擊後調用前述伺服器方法。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 流程,打造出效率與精準並存的新一代人機協作體驗!