各位专家、同仁,大家好。今天我们将深入探讨一个在人工智能与人类协作领域日益重要的概念:Human-Agent Collaborative Negotiation (HACON),即人机协作谈判。随着AI在复杂决策和自动化任务中的能力不断提升,我们发现纯粹的自动化并非总能达到最佳效果,尤其是在涉及高度不确定性、敏感性以及需要灵活应变的人际互动场景中。谈判,正是一个典型案例。
人机协作谈判的核心思想是:结合人类的直觉、经验、情感智能与代理(Agent)的计算能力、逻辑推理、数据处理速度。它不仅仅是让AI辅助人类,更强调在谈判过程中,人类可以随时介入、理解AI的决策逻辑、修改其策略参数,并让AI平滑地接管并继续执行。这种深度协作模式,旨在克服AI在理解细微社会信号、处理非结构化信息和进行创造性问题解决方面的局限,同时弥补人类在处理大量数据、快速计算复杂效用函数和保持客观性方面的不足。
1. 人机协作谈判的必要性与核心价值
在传统的自动化谈判中,一旦代理被启动,其行为往往是预设且封闭的。人类虽然设定了初始目标和约束,但对谈判过程的实时干预能力有限,这导致了几个问题:
- 缺乏透明度与信任: 人类难以理解代理为何做出某个提议,信任度自然降低。
- 僵化与不适应性: 预设策略可能无法应对突发情况或对手的非理性行为。
- 错过机会: 人类基于直觉和经验能捕捉到的潜在机会,代理可能因未编程或无法感知而错失。
- 责任归属模糊: 当谈判结果不尽如人意时,难以界定是人类设定的问题还是代理执行的问题。
人机协作谈判正是为了解决这些痛点。它通过提供一个透明、可控、可学习的框架,使得人类可以:
- 实时监控: 了解代理的当前状态、提议、对手的反应以及代理的内部决策逻辑。
- 策略调整: 根据实时反馈,动态修改代理的谈判策略、优先级或限制条件。
- 直接干预: 在关键时刻暂停代理、直接发出提议、接受或拒绝对方提议。
- 知识传承: 代理从人类的干预中学习,提升未来的谈判表现。
这种模式的核心价值在于,它将人类的战略智慧与AI的战术执行力无缝融合,形成一个超越个体能力的强大谈判实体。
2. HACON 架构的核心原则
为了实现上述目标,一个健壮的 HACON 架构必须遵循以下核心原则:
- 透明性 (Transparency): 代理的内部状态、决策过程、目标、约束和历史行为必须清晰地呈现给人类。
- 可解释性 (Explainability): 代理不仅要展示“做了什么”,还要解释“为什么这么做”,提供决策依据。
- 可干预性 (Intervenability): 人类必须能够在任何阶段暂停、修改、指导或直接接管代理的行动。
- 可修改性 (Modifiability): 人类能够方便地调整代理的策略参数、偏好、目标和优先级。
- 无缝衔接 (Seamless Continuation): 在人类干预后,代理能够平滑地整合人类的输入,并从中断处继续执行。
- 学习与适应 (Learning & Adaptation): 代理应从人类的干预和指导中学习,优化其未来行为。
3. 架构总览:HACON 系统组件
一个典型的人机协作谈判系统可以分解为以下核心组件。我们将通过一个概念性的表格来概览这些组件及其职责,随后深入探讨每个组件的设计与实现。
| 组件名称 | 主要职责 | 关键功能 |
|---|---|---|
| 用户界面 (UI) | 人机交互的门户,可视化代理状态与数据 | 展示谈判历史、当前提议、代理决策依据;提供干预操作入口 |
| 谈判代理核心 (Agent Core) | 协调所有模块,执行谈判逻辑 | 管理谈判生命周期、调用策略模块生成提议、处理对手响应 |
| 状态管理模块 (State Management) | 维护谈判的全局状态和历史信息 | 存储当前提议、对手提议、代理偏好、剩余时间、已达成共识的议题等 |
| 策略模块 (Strategy Module) | 封装各种谈判策略,根据状态生成提议或评估提议 | 实现让步策略、效用最大化策略、Tit-for-Tat等;动态切换策略 |
| 参数与约束管理模块 (Parameter & Constraint Mgmt) | 存储和管理代理的谈判参数与约束 | 维护保留价、目标价、让步率、议题权重、时间限制等;参数校验 |
| 干预与重载模块 (Intervention & Override Module) | 处理人类的干预指令,确保代理行为按人类意图执行 | 暂停/恢复代理、接受/拒绝提议、直接发出提议、修改策略参数、切换策略 |
| 通信与历史模块 (Communication & History) | 记录所有谈判事件,处理与外部谈判环境的通信 | 记录提议、反提议、决策、人类干预;与外部API/邮件/消息系统集成 |
| 学习与适应模块 (Learning & Adaptation) | 从人类干预中学习,优化代理行为 | 更新偏好模型、调整策略参数、识别人类偏好模式 |
| 外部API/数据集成 (External API/Data Integration) | 与外部数据源和服务交互,获取实时信息 | 获取市场价格、商品库存、竞争对手信息等 |
4. 详细设计与实现
现在,让我们深入到每个组件的设计与实现细节,并辅以代码示例。
4.1 用户界面 (UI) – 人类的窗口
UI是人类与HACON系统交互的唯一界面,其设计至关重要。它需要直观地展示复杂信息,并提供便捷的干预手段。
核心功能:
- 谈判概览: 显示当前谈判阶段、剩余时间、已达成的议题。
- 提议历史: 以时间线或列表形式展示所有提议(人类、代理、对手)。
- 当前提议与反提议: 清晰展示代理当前准备提出的提议和对手的最新提议。
- 代理决策依据: 解释代理为何做出当前提议(例如:基于效用函数、对手历史行为、当前策略)。
- 关键参数显示与修改: 显示保留价、目标价、议题权重、让步率等,并允许人类实时修改。
- 干预控制: 暂停/恢复按钮、接受/拒绝按钮、手动提议输入框、策略切换下拉菜单。
技术栈考量:
前端框架(React, Vue, Angular)结合 RESTful API 或 WebSocket 进行实时数据交互。
UI 数据结构示例 (JSON API 响应):
{
"negotiation_id": "NEG-20231027-001",
"status": "active",
"current_phase": "proposing_offer",
"time_remaining_seconds": 3600,
"our_last_offer": {
"price": 105.00,
"delivery_date": "2023-11-15",
"quantity": 500,
"payment_terms": "NET30"
},
"opponent_last_offer": {
"price": 110.00,
"delivery_date": "2023-11-30",
"quantity": 450,
"payment_terms": "NET60"
},
"agent_proposed_next_offer": {
"price": 107.50,
"delivery_date": "2023-11-20",
"quantity": 480,
"payment_terms": "NET45"
},
"agent_rationale": "基于当前让步策略,我们认为这是一个平衡的提议,旨在逐步接近对手的保留价,同时保留我们的核心利益。",
"key_parameters": {
"reservation_price": 100.00,
"aspiration_price": 120.00,
"concession_rate": 0.05,
"issue_weights": {
"price": 0.5,
"delivery_date": 0.2,
"quantity": 0.2,
"payment_terms": 0.1
},
"current_strategy": "gradual_concession"
},
"negotiation_history": [
// ... 历史提议和事件列表
],
"intervention_options": {
"can_pause": true,
"can_propose_manual_offer": true,
"can_accept": true,
"can_reject": true,
"can_modify_parameters": ["reservation_price", "concession_rate"],
"available_strategies": ["gradual_concession", "tit_for_tat", "aggressive_first"]
}
}
4.2 谈判代理核心 (Agent Core) – 大脑
谈判代理核心是整个系统的协调者和执行者,负责驱动谈判流程、调用其他模块完成具体任务。
核心职责:
- 生命周期管理: 初始化谈判、启动/暂停/恢复、终止谈判。
- 事件循环: 持续监听外部事件(对手提议、人类干预)。
- 决策调度: 根据当前状态和策略模块的输出,决定下一步行动(生成提议、接受、拒绝等)。
- 状态同步: 与状态管理模块交互,确保全局状态的最新和一致性。
Agent Core 结构示例 (Python):
from enum import Enum
import time
class NegotiationPhase(Enum):
INITIALIZING = "INITIALIZING"
WAITING_FOR_OPPONENT = "WAITING_FOR_OPPONENT"
PROPOSING_OFFER = "PROPOSING_OFFER"
EVALUATING_OPPONENT_OFFER = "EVALUATING_OPPONENT_OFFER"
HUMAN_INTERVENTION = "HUMAN_INTERVENTION"
NEGOTIATION_PAUSED = "NEGOTIATION_PAUSED"
AGREEMENT_REACHED = "AGREEMENT_REACHED"
IMPASSE_REACHED = "IMPASSE_REACHED"
class NegotiationAgentCore:
def __init__(self, state_manager, strategy_module, param_manager, intervention_manager, comm_history_module, learning_module):
self.state_manager = state_manager
self.strategy_module = strategy_module
self.param_manager = param_manager
self.intervention_manager = intervention_manager
self.comm_history_module = comm_history_module
self.learning_module = learning_module
self._is_running = False
self._current_phase = NegotiationPhase.INITIALIZING
def start_negotiation(self, initial_params, opponent_profile=None):
self.state_manager.initialize_state(initial_params, opponent_profile)
self.param_manager.load_parameters(initial_params)
self._is_running = True
self._current_phase = NegotiationPhase.PROPOSING_OFFER
self._negotiation_loop()
def pause_negotiation(self):
self._is_running = False
self._current_phase = NegotiationPhase.NEGOTIATION_PAUSED
self.comm_history_module.log_event("Human Paused Agent", {})
def resume_negotiation(self):
self._is_running = True
self.comm_history_module.log_event("Human Resumed Agent", {})
# 根据暂停前的状态决定从哪个阶段继续
if self.state_manager.get_current_state().opponent_last_offer:
self._current_phase = NegotiationPhase.EVALUATING_OPPONENT_OFFER
else:
self._current_phase = NegotiationPhase.PROPOSING_OFFER
self._negotiation_loop()
def handle_opponent_offer(self, offer):
self.state_manager.update_opponent_offer(offer)
self.comm_history_module.log_event("Opponent Offered", offer)
if self._is_running:
self._current_phase = NegotiationPhase.EVALUATING_OPPONENT_OFFER
# 立即触发一轮决策
self._negotiation_loop_step()
def _negotiation_loop(self):
while self._is_running and self._current_phase not in [NegotiationPhase.AGREEMENT_REACHED, NegotiationPhase.IMPASSE_REACHED]:
# 检查人类干预队列
if self.intervention_manager.has_pending_interventions():
self._current_phase = NegotiationPhase.HUMAN_INTERVENTION
self.intervention_manager.process_interventions(self) # 处理干预
# 干预处理后,agent可能被暂停或参数被修改,需要重新评估状态
if not self._is_running: # 如果干预导致agent暂停
break
self._current_phase = self._determine_next_phase_after_intervention()
self._negotiation_loop_step()
time.sleep(1) # 防止CPU空转,实际应用中可能更复杂,如基于事件驱动
def _negotiation_loop_step(self):
current_state = self.state_manager.get_current_state()
current_params = self.param_manager.get_current_parameters()
if self._current_phase == NegotiationPhase.PROPOSING_OFFER:
offer = self.strategy_module.generate_offer(current_state, current_params)
self.state_manager.update_our_offer(offer)
self.comm_history_module.log_event("Agent Proposed", offer)
self.comm_history_module.send_offer_to_opponent(offer) # 模拟发送
self._current_phase = NegotiationPhase.WAITING_FOR_OPPONENT
elif self._current_phase == NegotiationPhase.EVALUATING_OPPONENT_OFFER:
opponent_offer = current_state.opponent_last_offer
if self.strategy_module.evaluate_offer(opponent_offer, current_state, current_params):
self.accept_offer(opponent_offer)
else:
if self.strategy_module.should_reject_and_counter(opponent_offer, current_state, current_params):
self._current_phase = NegotiationPhase.PROPOSING_OFFER # 转入生成反提议
else:
# 无法达成一致,可能触发让步或寻求人类帮助
self.comm_history_module.log_event("Agent Unable to Resolve", {"reason": "Stalemate or complex situation"})
self._current_phase = NegotiationPhase.HUMAN_INTERVENTION # 请求人类介入
# 检查是否达成协议或僵局
if self.strategy_module.check_agreement(current_state, current_params):
self._current_phase = NegotiationPhase.AGREEMENT_REACHED
self._is_running = False
self.comm_history_module.log_event("Agreement Reached", current_state.last_agreed_offer)
elif self.strategy_module.check_impasse(current_state, current_params):
self._current_phase = NegotiationPhase.IMPASSE_REACHED
self._is_running = False
self.comm_history_module.log_event("Impasse Reached", {})
def accept_offer(self, offer):
self.state_manager.record_agreement(offer)
self.comm_history_module.log_event("Agent Accepted Offer", offer)
self._current_phase = NegotiationPhase.AGREEMENT_REACHED
self._is_running = False
def reject_offer(self):
self.comm_history_module.log_event("Agent Rejected Offer", self.state_manager.get_current_state().opponent_last_offer)
self.state_manager.clear_opponent_offer() # 清除待处理的对手提议
self._current_phase = NegotiationPhase.PROPOSING_OFFER # 准备生成新的反提议
def _determine_next_phase_after_intervention(self):
# 简单逻辑:如果有人工指令生成新offer,则进入PROPOSING_OFFER,否则根据状态判断
if self.intervention_manager.has_manual_offer_pending():
return NegotiationPhase.PROPOSING_OFFER
elif self.state_manager.get_current_state().opponent_last_offer:
return NegotiationPhase.EVALUATING_OPPONENT_OFFER
else:
return NegotiationPhase.PROPOSING_OFFER
4.3 状态管理模块 (State Management) – 记忆与上下文
状态管理模块是HACON系统的核心记忆,它维护着谈判过程中的所有关键信息,确保在任何时间点,系统都能获取到最新、最准确的谈判上下文。这对于人类的介入和代理的无缝恢复至关重要。
存储内容:
- 谈判元数据:
negotiation_id,status(active, paused, completed),start_time,end_time。 - 议题定义: 谈判涉及的所有议题及其可能的值域(例如:价格范围、交付日期列表、数量范围)。
- 代理偏好与效用: 代理(即我方)对每个议题的偏好、权重、保留价、目标价。
- 对手信息: 已知或推断的对手偏好、历史行为、最新提议。
- 提议历史: 每次我方和对手的提议,包括提议内容、时间、提议方。
- 当前状态:
our_last_offer,opponent_last_offer,current_negotiation_round。 - 已达成共识的议题: 如果是多议题谈判,记录已达成一致的议题部分。
实现方式:
通常使用数据库(如PostgreSQL、MongoDB)进行持久化存储,结合内存缓存提高读取性能。
NegotiationState 数据模型示例 (Python):
import datetime
from typing import Dict, Any, List, Optional
class Offer:
def __init__(self, price: float, delivery_date: str, quantity: int, payment_terms: str):
self.price = price
self.delivery_date = delivery_date # e.g., "YYYY-MM-DD"
self.quantity = quantity
self.payment_terms = payment_terms # e.g., "NET30", "NET60"
def to_dict(self):
return self.__dict__
@staticmethod
def from_dict(data: Dict[str, Any]):
return Offer(**data)
class NegotiationState:
def __init__(self, negotiation_id: str, initial_params: Dict[str, Any], opponent_profile: Optional[Dict[str, Any]] = None):
self.negotiation_id = negotiation_id
self.status: str = "active" # "active", "paused", "completed", "impasse"
self.current_round: int = 0
self.start_time: datetime.datetime = datetime.datetime.now()
self.end_time: Optional[datetime.datetime] = None
self.our_initial_params = initial_params # 初始设定的参数
self.opponent_profile = opponent_profile if opponent_profile else {} # 对手信息
self.our_last_offer: Optional[Offer] = None
self.opponent_last_offer: Optional[Offer] = None
self.last_agreed_offer: Optional[Offer] = None # 如果达成协议
self.offer_history: List[Dict[str, Any]] = [] # 记录所有提议及发出方
# 议题定义,例如:
self.issues: Dict[str, Any] = {
"price": {"min": 90.0, "max": 120.0, "step": 0.5},
"delivery_date": {"options": ["2023-11-15", "2023-11-20", "2023-11-30"]},
"quantity": {"min": 400, "max": 600, "step": 10},
"payment_terms": {"options": ["NET15", "NET30", "NET45", "NET60"]}
}
def update_our_offer(self, offer: Offer):
self.our_last_offer = offer
self.offer_history.append({"source": "agent", "offer": offer.to_dict(), "timestamp": datetime.datetime.now()})
self.current_round += 1
def update_opponent_offer(self, offer: Offer):
self.opponent_last_offer = offer
self.offer_history.append({"source": "opponent", "offer": offer.to_dict(), "timestamp": datetime.datetime.now()})
def record_agreement(self, offer: Offer):
self.last_agreed_offer = offer
self.status = "completed"
self.end_time = datetime.datetime.now()
def clear_opponent_offer(self):
self.opponent_last_offer = None
def to_dict(self):
# 序列化状态以便存储或传输
data = self.__dict__.copy()
if data['our_last_offer']: data['our_last_offer'] = data['our_last_offer'].to_dict()
if data['opponent_last_offer']: data['opponent_last_offer'] = data['opponent_last_offer'].to_dict()
if data['last_agreed_offer']: data['last_agreed_offer'] = data['last_agreed_offer'].to_dict()
data['start_time'] = data['start_time'].isoformat()
if data['end_time']: data['end_time'] = data['end_time'].isoformat()
return data
@staticmethod
def from_dict(data: Dict[str, Any]):
# 反序列化
state = NegotiationState(data['negotiation_id'], data['our_initial_params'], data['opponent_profile'])
for key, value in data.items():
if key == 'our_last_offer' and value:
state.our_last_offer = Offer.from_dict(value)
elif key == 'opponent_last_offer' and value:
state.opponent_last_offer = Offer.from_dict(value)
elif key == 'last_agreed_offer' and value:
state.last_agreed_offer = Offer.from_dict(value)
elif key == 'start_time':
state.start_time = datetime.datetime.fromisoformat(value)
elif key == 'end_time' and value:
state.end_time = datetime.datetime.fromisoformat(value)
elif key in ['our_initial_params', 'opponent_profile', 'offer_history', 'issues', 'negotiation_id', 'status', 'current_round']:
setattr(state, key, value)
return state
4.4 策略模块 (Strategy Module) – 决策者
策略模块是代理智能的核心,负责根据当前谈判状态、参数和对手行为,生成提议或评估对手提议。
核心功能:
- 提议生成: 根据当前策略,计算并生成一个包含所有议题值的新提议。
- 提议评估: 评估对手的提议对我方的效用,以及与我方保留价的差距。
- 策略切换: 允许代理或人类动态切换不同的谈判策略。
- 解释性: 提供策略生成提议的逻辑解释。
策略类型示例:
- 让步策略 (Concession Strategy): 随着时间或轮次逐步向对手让步。
- 效用最大化策略 (Utility Maximization): 尝试提出一个对我方效用最高且对手可能接受的提议。
- Tit-for-Tat 策略: 模仿对手的让步行为。
- 时间敏感策略: 随着谈判截止日期临近,加快让步速度。
抽象策略类与具体策略示例 (Python):
from abc import ABC, abstractmethod
import random
from typing import Tuple, Dict, Any
# 假设Offer和NegotiationState类已定义
class NegotiationStrategy(ABC):
@abstractmethod
def generate_offer(self, state: 'NegotiationState', params: 'NegotiationParameters') -> Offer:
"""根据当前状态和参数生成提议。"""
pass
@abstract
def evaluate_offer(self, offer: Offer, state: 'NegotiationState', params: 'NegotiationParameters') -> bool:
"""评估对手提议是否可接受。"""
pass
@abstract
def get_rationale(self) -> str:
"""返回当前策略生成提议的解释。"""
pass
def check_agreement(self, state: 'NegotiationState', params: 'NegotiationParameters') -> bool:
"""检查是否达成协议。"""
if state.opponent_last_offer:
return self.evaluate_offer(state.opponent_last_offer, state, params) and
self._evaluate_offer_from_opponent_perspective(state.our_last_offer, state, params) # 假设我们能推断对手是否接受
return False
def check_impasse(self, state: 'NegotiationState', params: 'NegotiationParameters') -> bool:
"""检查是否陷入僵局。"""
# 简单示例:如果达到最大轮次或价格已低于保留价
if state.current_round >= params.max_rounds:
return True
if state.our_last_offer and state.our_last_offer.price < params.reservation_price:
return True # 如果提议价格低于保留价,且对手不接受
return False
def should_reject_and_counter(self, opponent_offer: Offer, state: 'NegotiationState', params: 'NegotiationParameters') -> bool:
"""判断是否应该拒绝并生成反提议。"""
return not self.evaluate_offer(opponent_offer, state, params)
@abstract
def _calculate_utility(self, offer: Offer, params: 'NegotiationParameters') -> float:
"""计算给定提议对我方的效用。"""
pass
def _evaluate_offer_from_opponent_perspective(self, offer: Offer, state: 'NegotiationState', params: 'NegotiationParameters') -> bool:
"""
尝试评估一个提议是否对对手而言是可接受的。
这通常需要对手模型或推断,这里简化为假设对手有自己的保留价。
"""
# 实际中会更复杂,可能需要学习对手的效用函数
# 假设对手的保留价高于某个值
if offer.price > params.estimated_opponent_reservation_price:
return True
return False
class GradualConcessionStrategy(NegotiationStrategy):
def __init__(self):
self.rationale = "当前采用逐步让步策略,旨在通过小幅让步逐步接近对手,同时避免过快暴露底线。"
def generate_offer(self, state: 'NegotiationState', params: 'NegotiationParameters') -> Offer:
# 基于上次提议或初始目标进行让步
current_offer_data = state.our_last_offer.to_dict() if state.our_last_offer else params.aspiration_offer.to_dict()
# 假设价格是主要让步议题
current_price = current_offer_data['price']
# 计算让步步长,例如基于让步率和价格范围
price_range = state.issues['price']['max'] - state.issues['price']['min']
concession_step = price_range * params.concession_rate * (state.current_round + 1) / params.max_rounds # 随着轮次增加让步幅度
new_price = max(params.reservation_price, current_price - concession_step) # 确保不低于保留价
# 其他议题可以根据权重和偏好进行调整,这里简化
new_delivery_date = current_offer_data['delivery_date']
new_quantity = current_offer_data['quantity']
new_payment_terms = current_offer_data['payment_terms']
return Offer(new_price, new_delivery_date, new_quantity, new_payment_terms)
def evaluate_offer(self, offer: Offer, state: 'NegotiationState', params: 'NegotiationParameters') -> bool:
# 检查是否高于保留价,并且效用是否足够
return offer.price >= params.reservation_price and self._calculate_utility(offer, params) >= params.minimum_acceptable_utility
def _calculate_utility(self, offer: Offer, params: 'NegotiationParameters') -> float:
# 简单效用函数:价格权重最高,其他次之
utility = 0.0
# 价格效用 (越高越好)
price_utility = (offer.price - params.reservation_price) / (params.aspiration_price - params.reservation_price)
utility += price_utility * params.issue_weights.get("price", 0.5)
# 交付日期(越早越好,这里简化为固定值)
# 如果 offer.delivery_date == params.preferred_delivery_date_early: utility += weight * 1
# 如果 offer.delivery_date == params.preferred_delivery_date_late: utility += weight * 0.5
utility += params.issue_weights.get("delivery_date", 0.2) * (1 if offer.delivery_date == "2023-11-15" else 0.5)
# 数量(越多越好)
quantity_utility = (offer.quantity - 400) / (600 - 400) # 假设400-600是范围
utility += quantity_utility * params.issue_weights.get("quantity", 0.2)
# 支付条款(越快越好)
payment_terms_map = {"NET15": 1.0, "NET30": 0.7, "NET45": 0.4, "NET60": 0.1}
utility += payment_terms_map.get(offer.payment_terms, 0.0) * params.issue_weights.get("payment_terms", 0.1)
return utility
def get_rationale(self) -> str:
return self.rationale
# 策略模块可以包含一个字典来存储所有可用策略
class StrategyModule:
def __init__(self):
self.strategies: Dict[str, NegotiationStrategy] = {
"gradual_concession": GradualConcessionStrategy(),
# "tit_for_tat": TitForTatStrategy(), # 可以添加其他策略
}
self.current_strategy_name: str = "gradual_concession"
def set_strategy(self, strategy_name: str):
if strategy_name in self.strategies:
self.current_strategy_name = strategy_name
print(f"Strategy changed to: {strategy_name}")
else:
raise ValueError(f"Unknown strategy: {strategy_name}")
def get_current_strategy(self) -> NegotiationStrategy:
return self.strategies[self.current_strategy_name]
def generate_offer(self, state: 'NegotiationState', params: 'NegotiationParameters') -> Offer:
return self.get_current_strategy().generate_offer(state, params)
def evaluate_offer(self, offer: Offer, state: 'NegotiationState', params: 'NegotiationParameters') -> bool:
return self.get_current_strategy().evaluate_offer(offer, state, params)
def get_rationale(self) -> str:
return self.get_current_strategy().get_rationale()
def check_agreement(self, state: 'NegotiationState', params: 'NegotiationParameters') -> bool:
return self.get_current_strategy().check_agreement(state, params)
def check_impasse(self, state: 'NegotiationState', params: 'NegotiationParameters') -> bool:
return self.get_current_strategy().check_impasse(state, params)
def should_reject_and_counter(self, opponent_offer: Offer, state: 'NegotiationState', params: 'NegotiationParameters') -> bool:
return self.get_current_strategy().should_reject_and_counter(opponent_offer, state, params)
4.5 参数与约束管理模块 (Parameter & Constraint Management) – 规则引擎
此模块负责存储、验证和管理所有影响代理行为的参数和约束。人类通过UI修改这些参数,直接影响代理的决策。
参数类型:
- 目标参数:
aspiration_price(期望价格),aspiration_delivery_date等。 - 保留参数:
reservation_price(底线价格),minimum_quantity等。 - 策略参数:
concession_rate(让步速度),aggressiveness_level。 - 效用参数:
issue_weights(各议题重要性权重),utility_function_coeffs。 - 时间/轮次参数:
max_rounds,time_limit_seconds。
约束类型:
- 硬约束: 不可谈判的底线(例如:价格不能低于成本)。
- 软约束: 代理应尽量遵守,但可在人类干预下打破的指导原则。
实现方式:
一个专门的类来封装所有参数,提供校验方法。参数可以从配置文件、数据库或UI动态加载和更新。
NegotiationParameters 数据结构示例 (Python):
from typing import Dict, Any, Optional
class NegotiationParameters:
def __init__(self,
reservation_price: float,
aspiration_price: float,
concession_rate: float,
issue_weights: Dict[str, float],
max_rounds: int,
time_limit_seconds: int,
aspiration_offer: Offer, # 初始期望提议
minimum_acceptable_utility: float = 0.6, # 代理认为可接受的最低效用值
estimated_opponent_reservation_price: float = 100.0 # 用于模拟对手接受度的估计
):
self.reservation_price = reservation_price
self.aspiration_price = aspiration_price
self.concession_rate = concession_rate
self.issue_weights = issue_weights # {'price': 0.5, 'delivery_date': 0.2, ...}
self.max_rounds = max_rounds
self.time_limit_seconds = time_limit_seconds
self.aspiration_offer = aspiration_offer
self.minimum_acceptable_utility = minimum_acceptable_utility
self.estimated_opponent_reservation_price = estimated_opponent_reservation_price
self._validate_parameters()
def _validate_parameters(self):
if not (0 <= self.concession_rate <= 1):
raise ValueError("Concession rate must be between 0 and 1.")
if not (self.reservation_price <= self.aspiration_price):
raise ValueError("Reservation price must be less than or equal to aspiration price.")
if abs(sum(self.issue_weights.values()) - 1.0) > 1e-6:
# print("Warning: Issue weights do not sum to 1.0. Normalizing.")
total_weight = sum(self.issue_weights.values())
if total_weight > 0:
self.issue_weights = {k: v / total_weight for k, v in self.issue_weights.items()}
else:
raise ValueError("Issue weights cannot all be zero.")
# 更多针对具体参数的校验
def update_parameter(self, param_name: str, value: Any):
if hasattr(self, param_name):
setattr(self, param_name, value)
self._validate_parameters() # 每次更新后重新校验
else:
raise AttributeError(f"Parameter '{param_name}' not found.")
def to_dict(self):
data = self.__dict__.copy()
data['aspiration_offer'] = data['aspiration_offer'].to_dict()
return data
@staticmethod
def from_dict(data: Dict[str, Any]):
data['aspiration_offer'] = Offer.from_dict(data['aspiration_offer'])
return NegotiationParameters(**data)
class ParameterManager:
def __init__(self):
self._current_params: Optional[NegotiationParameters] = None
def load_parameters(self, initial_params_dict: Dict[str, Any]):
# 假设 initial_params_dict 包含了所有构造 NegotiationParameters 所需的数据
self._current_params = NegotiationParameters.from_dict(initial_params_dict)
def get_current_parameters(self) -> NegotiationParameters:
if not self._current_params:
raise RuntimeError("Negotiation parameters not loaded.")
return self._current_params
def update_parameter_by_human(self, param_name: str, value: Any):
if self._current_params:
self._current_params.update_parameter(param_name, value)
# 记录人类修改事件
# self.comm_history_module.log_event("Human Modified Parameter", {"param": param_name, "new_value": value})
print(f"Parameter '{param_name}' updated to {value} by human.")
else:
raise RuntimeError("Cannot update parameters: Negotiation not initialized.")
4.6 干预与重载模块 (Intervention & Override Module) – 控制面板
这是 HACON 架构的核心创新点之一,它提供了人类介入和控制代理行为的机制。
核心功能:
- 指令队列: 接收并排队处理来自UI的人类干预指令。
- 暂停/恢复: 允许人类暂停代理的自主运行,检查状态,进行修改,然后恢复。
- 直接提议: 人类可以直接输入一个提议,代理将立即发出。
- 接受/拒绝: 人类可以Override代理的决策,直接接受或拒绝对手的提议。
- 参数/策略修改: 转发人类对参数和策略的修改指令给相应模块。
- 冲突解决: 处理人类指令与代理当前状态或策略可能产生的冲突。
实现方式:
一个独立的模块,通过一个线程安全的队列来接收干预指令,并在代理的核心循环中定期检查和处理。
InterventionManager 示例 (Python):
import queue
from typing import Dict, Any, Callable
class InterventionType:
PAUSE = "PAUSE"
RESUME = "RESUME"
MANUAL_OFFER = "MANUAL_OFFER"
ACCEPT_OPPONENT_OFFER = "ACCEPT_OPPONENT_OFFER"
REJECT_OPPONENT_OFFER = "REJECT_OPPONENT_OFFER"
MODIFY_PARAMETER = "MODIFY_PARAMETER"
CHANGE_STRATEGY = "CHANGE_STRATEGY"
REQUEST_RATIONALE = "REQUEST_RATIONALE"
class InterventionManager:
def __init__(self, state_manager, param_manager, strategy_module, comm_history_module):
self.intervention_queue = queue.Queue()
self.state_manager = state_manager
self.param_manager = param_manager
self.strategy_module = strategy_module
self.comm_history_module = comm_history_module
self._has_manual_offer_pending = False # 标记是否有手动提议待处理
def add_intervention(self, intervention_type: str, payload: Dict[str, Any] = None):
"""从UI或其他外部源添加干预指令。"""
if payload is None:
payload = {}
self.intervention_queue.put({"type": intervention_type, "payload": payload})
self.comm_history_module.log_event("Human Intervention Added", {"type": intervention_type, "payload": payload})
print(f"Intervention added: {intervention_type} with payload {payload}")
def has_pending_interventions(self) -> bool:
return not self.intervention_queue.empty()
def has_manual_offer_pending(self) -> bool:
return self._has_manual_offer_pending
def process_interventions(self, agent_core: 'NegotiationAgentCore'):
"""在代理的核心循环中调用,处理队列中的所有干预。"""
while not self.intervention_queue.empty():
intervention = self.intervention_queue.get()
intervention_type = intervention["type"]
payload = intervention["payload"]
self._has_manual_offer_pending = False # 每次处理前重置
print(f"Processing intervention: {intervention_type}")
if intervention_type == InterventionType.PAUSE:
agent_core.pause_negotiation()
elif intervention_type == InterventionType.RESUME:
agent_core.resume_negotiation()
elif intervention_type == InterventionType.MANUAL_OFFER:
try:
manual_offer = Offer.from_dict(payload["offer"])
self.state_manager.update_our_offer(manual_offer) # 更新代理状态
self.comm_history_module.log_event("Human Proposed Manual Offer", manual_offer.to_dict())
self.comm_history_module.send_offer_to_opponent(manual_offer) # 直接发送
self._has_manual_offer_pending = True # 标记有手动提议
# 让代理核心知道提议已发出,无需再生成
# agent_core._current_phase = NegotiationPhase.WAITING_FOR_OPPONENT # 直接进入等待对手阶段
except Exception as e:
self.comm_history_module.log_event("Manual Offer Failed", {"error": str(e), "payload": payload})
print(f"Error processing manual offer: {e}")
elif intervention_type == InterventionType.ACCEPT_OPPONENT_OFFER:
opponent_offer = self.state_manager.get_current_state().opponent_last_offer
if opponent_offer:
agent_core.accept_offer(opponent_offer)
else:
self.comm_history_module.log_event("Accept Offer Failed", {"reason": "No opponent offer to accept"})
elif intervention_type == InterventionType.REJECT_OPPONENT_OFFER:
agent_core.reject_offer()
elif intervention_type == InterventionType.MODIFY_PARAMETER:
try:
self.param_manager.update_parameter_by_human(payload["param_name"], payload["value"])
except Exception as e:
self.comm_history_module.log_event("Modify Parameter Failed", {"error": str(e), "payload": payload})
print(f"Error modifying parameter: {e}")
elif intervention_type == InterventionType.CHANGE_STRATEGY:
try:
self.strategy_module.set_strategy(payload["strategy_name"])
except Exception as e:
self.comm_history_module.log_event("Change Strategy Failed", {"error": str(e), "payload": payload})
print(f"Error changing strategy: {e}")
elif intervention_type == InterventionType.REQUEST_RATIONALE:
rationale = self.strategy_module.get_rationale()
self.comm_history_module.log_event("Agent Rationale Requested", {"rationale": rationale})
# 可以将此信息推送到UI
print(f"Agent Rationale: {rationale}")
else:
self.comm_history_module.log_event("Unknown Intervention", {"type": intervention_type, "payload": payload})
print(f"Unknown intervention type: {intervention_type}")
4.7 通信与历史模块 (Communication & History) – 日志与桥梁
该模块负责所有内部和外部通信,以及详细记录谈判过程中的所有事件,包括代理决策、人类干预和对手行为。
核心功能:
- 事件日志: 记录所有提议、反提议、接受、拒绝、策略切换、参数修改、暂停/恢复等。
- 外部通信: 与实际谈判对手进行通信(模拟API、邮件、消息平台等)。
- 内部消息: 在各模块间传递状态更新或指令(例如:Agent Core通知UI刷新数据)。
- 可审计性: 确保所有关键事件都可追溯。
实现方式:
日志系统(如ELK Stack)、消息队列(如RabbitMQ, Kafka)用于异步通信和事件流处理,RESTful API用于与UI或外部系统集成。
NegotiationEvent 数据模型与 CommunicationHistoryModule 示例 (Python):
import datetime
from typing import Dict, Any, List
class NegotiationEvent:
def __init__(self, event_type: str, timestamp: datetime.datetime, details: Dict[str, Any]):
self.event_type = event_type
self.timestamp = timestamp
self.details = details
def to_dict(self):
return {
"event_type": self.event_type,
"timestamp": self.timestamp.isoformat(),
"details": self.details
}
@staticmethod
def from_dict(data: Dict[str, Any]):
return NegotiationEvent(
data["event_type"],
datetime.datetime.fromisoformat(data["timestamp"]),
data["details"]
)
class CommunicationHistoryModule:
def __init__(self, negotiation_id: str):
self.negotiation_id = negotiation_id
self.event_log: List[NegotiationEvent] = []
# 可以集成到数据库或日志系统
print(f"CommunicationHistoryModule initialized for negotiation: {negotiation_id}")
def log_event(self, event_type: str, details: Dict[str, Any]):
"""记录所有内部和外部事件。"""
event = NegotiationEvent(event_type, datetime.datetime.now(), details)
self.event_log.append(event)
# 实际应用中会写入数据库或消息队列
# print(f"LOG - {event.timestamp}: {event.type} - {event.details}")
def get_negotiation_history(self) -> List[NegotiationEvent]:
"""获取完整的谈判历史记录。"""
return self.event_log
def send_offer_to_opponent(self, offer: Offer):
"""模拟向对手发送提议。"""
# 实际中会调用外部API (e.g., email, chat, dedicated negotiation platform)
self.log_event("Sent Offer to Opponent", offer.to_dict())
print(f"Simulating sending offer to opponent: {offer.to_dict()}")
# 更多方法,例如接收对手消息,发布UI更新事件等
def publish_ui_update(self, data: Dict[str, Any]):
"""模拟向UI发布实时更新,实际可能通过WebSocket。"""
# print(f"Publishing UI update: {data}")
pass
4.8 学习与适应模块 (Learning & Adaptation) – 学习者
这个模块负责从人类的干预中学习,以提高代理未来的表现。这是HACON区别于简单自动化系统的关键特征。
学习机制:
- 偏好模型更新: 当人类修改议题权重、保留价或接受/拒绝某个提议时,学习模块可以更新代理对自身偏好的理解。
- 策略参数调整: 观察人类在特定情境下如何调整让步率、攻击性等策略参数,并进行泛化。
- 对手模型学习: 如果人类能提供关于对手的额外信息,或通过干预纠正代理对对手的误判,则学习模块可以改进对手模型。
- 模仿学习/强化学习: 将人类的干预视为“专家演示”,通过模仿学习训练代理在类似情境下采取人类偏好的行动;或将人类的反馈作为奖励信号,通过强化学习优化策略。
实现方式:
结合机器学习技术。例如,使用朴素贝叶斯、决策树或神经网络来预测人类在特定状态下可能采取的行动,并调整策略。
LearningComponent 示例 (Python):
from typing import Dict, Any, List
import pandas as pd
from sklearn.ensemble import RandomForestClassifier # 示例:用于预测人类行为或调整参数
class LearningComponent:
def __init__(self, param_manager, strategy_module):
self.param_manager = param_manager
self.strategy_module = strategy_module
self.human_intervention_data: List[Dict[str, Any]] = []
self.model = None # 机器学习模型
def record_human_intervention(self, state: 'NegotiationState', intervention_details: Dict[str, Any]):
"""记录人类干预时的上下文和干预内容。"""
data = {
"negotiation_id": state.negotiation_id,
"current_round": state.current_round,
"our_last_offer_price": state.our_last_offer.price if state.our_last_offer else None,
"opponent_last_offer_price": state.opponent_last_offer.price if state.opponent_last_offer else None,
"agent_proposed_next_offer_price": self.strategy_module.generate_offer(state, self.param_manager.get_current_parameters()).price, # 代理本应提出的价格
"intervention_type": intervention_details["type"],
"intervention_payload": intervention_details["payload"] # 详细的干预内容
}
self.human_intervention_data.append(data)
# 可以在这里触发轻量级即时学习
self._instant_adapt_from_human_action(state, intervention_details)
def _instant_adapt_from_human_action(self, state: 'NegotiationState', intervention_details: Dict[str, Any]):
"""从单次人类干预中进行即时、启发式学习。"""
intervention_type = intervention_details["type"]
payload = intervention_details["payload"]
if intervention_type == InterventionType.MODIFY_PARAMETER:
param_name = payload["param_name"]
value = payload["value"]
# 如果人类修改了保留价,这可能意味着代理对保留价的估计有误
if param_name == "reservation_price":
print(f"Learning: Human adjusted reservation price to {value}. Agent should re-evaluate its bottom line.")
# 实际中,可以更新一个内部的“人类保留价偏好模型”
elif param_name == "concession_rate":
print(f"Learning: Human adjusted concession rate to {value}. Agent should adjust its pacing.")
# 可以微调默认的让步策略参数
elif intervention_type == InterventionType.MANUAL_OFFER:
manual_offer = Offer.from_dict(payload["offer"])
# 比较人类手动提议与代理本应提出的提议
agent_expected_offer = self.strategy_module.generate_offer(state, self.param_manager.get_current_parameters())
if manual_offer.price != agent_expected_offer.price:
print(f"Learning: Human proposed a different offer (price: {manual_offer.price}) than agent (price: {agent_expected_offer.price}).")
# 分析差异,理解人类意图,例如:人类更关注短期利益,或对某个议题有更强烈的偏好
# 可以更新 issue_weights 或尝试理解人类的效用函数
elif intervention_type == InterventionType.ACCEPT_OPPONENT_OFFER:
# 如果代理评估为不接受但人类接受了,说明代理的效用函数或保留价可能过于保守
print("Learning: Human accepted an offer agent would have rejected. Agent's acceptable utility threshold may need adjustment.")
# 可以微调 minimum_acceptable_utility
def train_model_from_history(self):
"""批量训练模型,从历史干预数据中学习更复杂的模式。"""
if not self.human_intervention_data:
print("No human intervention data to train on.")
return
df = pd.DataFrame(self.human_intervention_data)
# 示例:预测在特定状态下人类是否会修改某个参数
# 假设我们想预测人类是否会修改 'reservation_price'
df['will_modify_reservation_price'] = df['intervention_payload'].apply(
lambda x: x.get('param_name') == 'reservation_price' if x and x.get('type') == InterventionType.MODIFY_PARAMETER else False
)
# 准备特征 (简化)
features = ['current_round', 'our_last_offer_price', 'opponent_last_offer_price']
df_cleaned = df.dropna(subset=features)
if df_cleaned.empty:
print("Not enough clean data for training.")
return
X = df_cleaned[features]
y = df_cleaned['will_modify_reservation_price']
if len(y.unique()) < 2:
print("Target variable has only one class, cannot train classifier.")
return
self.model = RandomForestClassifier(n_estimators=100, random_state=42)
self.model.fit(X, y)
print("Learning model trained successfully.")
def predict_human_preference(self, state: 'NegotiationState') -> Dict[str, Any]:
"""预测人类在当前状态下可能更倾向于的行动或参数。"""
if not self.model:
return {"prediction_status": "No model trained."}
current_data = {
"current_round": state.current_round,
"our_last_offer_price": state.our_last_offer.price if state.our_last_offer else None,
"opponent_last_offer_price": state.opponent_last_offer.price if state.opponent_last_offer else None,
}
# 确保数据格式与训练时一致
current_df = pd.DataFrame([current_data])
current_df = current_df.fillna(0) # 简单处理NaNs
prediction = self.model.predict(current_df)
prediction_proba = self.model.predict_proba(current_df)
return {
"prediction_status": "success",
"will_modify_reservation_price": bool(prediction[0]),
"probability": prediction_proba[0].tolist()
}
4.9 外部API/数据集成 (External API/Data Integration) – 桥接到世界
此模块负责与 HACON 系统外部的各种服务和数据源进行交互,提供代理做出明智决策所需的实时信息。
集成示例:
- 市场数据API: 获取商品最新价格、库存信息、汇率等。
- 企业内部系统: 访问库存管理、CRM、财务系统,获取成本、客户历史、预算等信息。
- 通信平台API: 与电子邮件、Slack、Teams等集成,发送或接收谈判消息。
- 身份验证/授权服务: 确保安全访问外部资源。
ExternalAPIClient 示例 (Python):
import requests
import json
from typing import Dict, Any, Optional
class ExternalAPIClient:
def __init__(self, config: Dict[str, Any]):
self.market_data_url = config.get("market_data_url")
self.crm_api_key = config.get("crm_api_key")
# ... 其他API配置
def get_realtime_market_price(self, item_id: str) -> Optional[float]:
"""从外部市场数据API获取实时价格。"""
if not self.market_data_url:
print("Market data URL not configured.")
return None
try:
response = requests.get(f"{self.market_data_url}/price/{item_id}", timeout=5)
response.raise_for_status() # Raises HTTPError for bad responses (4xx or 5xx)
data = response.json()
return data.get("price")
except requests.exceptions.RequestException as e:
print(f"Error fetching market price for {item_id}: {e}")
return None
def get_customer_history(self, customer_id: str) -> Optional[Dict[str, Any]]:
"""从CRM系统获取客户历史信息。"""
# 模拟调用CRM API
if not self.crm_api_key:
print("CRM API key not configured.")
return None
print(f"Fetching CRM history for customer {customer_id} using key {self.crm_api_key}...")
# 实际中会是带认证的API调用
return {"customer_id": customer_id, "previous_deals": [{"amount": 1000, "date": "2023-01-01"}], "rating": 4.5}
def send_negotiation_email(self, recipient: str, subject: str, body: str):
"""模拟通过邮件服务发送谈判邮件。"""
print(f"Sending email to {recipient} with subject '{subject}'...")
# 实际中会调用邮件API (e.g., SendGrid, Mailgun)
return {"status": "success", "message_id": "mock_email_id_123"}
5. 人机协作谈判的工作流程
了解了各个组件之后,我们来梳理一个典型的 HACON 工作流程:
| 阶段 | 描述 | 关键交互 |
|---|---|---|
| 1. 初始化设置 | 人类通过UI定义谈判的初始目标、保留价、议题权重、首选策略等参数。 | UI输入,参数管理器存储,代理核心初始化。 |
| 2. 自主执行 | 代理根据设定的策略和参数,自主地与对手进行谈判。UI实时显示代理的提议、对手的响应、代理的决策依据以及当前状态。 | 代理核心调用策略模块生成提议,状态管理器更新,通信模块发送/接收,UI实时更新。 |
| 3. 观察与预警 | 人类观察谈判进展。如果代理遇到僵局、提议效果不佳或达到预设的预警条件(如价格接近保留价),UI会发出提醒。 | UI展示,代理核心内部监控状态,学习模块可能提供潜在问题预警。 |
| 4. 人类干预 | 人类决定介入。通过UI暂停代理,修改参数(如提高让步率、调整议题权重),切换策略,甚至直接发出一个人工提议。 | UI发送干预指令,干预与重载模块接收并排队,代理核心暂停并处理指令。 |
| 5. 整合与恢复 | 代理整合人类的修改。例如,如果人类修改了让步率,代理下次生成提议时会应用新的让步率。如果人类发出了人工提议,代理会将其作为自己的提议。 | 干预与重载模块更新状态管理器和参数管理器,代理核心依据新状态和参数继续执行。 |
| 6. 学习与优化 | 代理将人类的干预行为作为经验数据记录下来。学习模块分析这些数据,以优化代理未来的决策逻辑和参数。 | 学习模块记录干预事件,定期或实时更新偏好模型和策略参数。 |
| 7. 达成协议/终止 | 谈判达成协议,或达到时间/轮次限制而终止。系统记录最终结果,并通知人类。 | 代理核心识别协议或僵局,状态管理器更新最终状态,通信模块通知人类和外部系统。 |
6. 技术栈考量
构建这样一个复杂的HACON系统,需要一个多功能的技术栈:
- 后端服务: Python (FastAPI/Flask/Django) 或 Node.js (Express) 是理想选择,它们拥有丰富的AI/ML库和强大的Web开发能力。Java (Spring Boot) 也是企业级应用中的有力竞争者。
- 前端界面: React, Vue.js, Angular 等现代前端框架,提供响应式、交互性强的用户体验。
- 数据库: PostgreSQL (关系型,适用于复杂数据结构和事务) 或 MongoDB (文档型,适用于灵活数据模型)。
- 消息队列: RabbitMQ 或 Kafka,用于解耦组件、处理异步任务和实现实时事件流。
- 机器学习库: Scikit-learn, TensorFlow, PyTorch,用于学习模块的开发。
- 容器化与编排: Docker 和 Kubernetes,用于部署、扩展和管理微服务。
- 版本控制: Git。
7. 挑战与未来方向
人机协作谈判仍然是一个活跃的研究领域,面临诸多挑战:
- 信任与可解释性: 如何在不淹没人类的情况下,提供足够的透明度和决策解释,以建立和维护人类对代理的信任?
- 认知负荷管理: 平衡代理提供的信息量与人类处理这些信息的认知负荷。过多的信息会导致人类疲劳,过少则影响决策。
- 实时适应与学习: 如何让代理在实时、动态的谈判环境中,更快速、更智能地从人类的干预中学习并适应?
- 多模态交互: 结合语音、图像、手势等信息,使人机交互更加自然。
- 伦理与偏见: 确保代理在学习过程中不引入或放大偏见,并遵守伦理原则。
- 通用性与领域特定性: 如何设计一个既具有一定通用性,又能针对特定谈判领域(如销售、采购、法律)进行高效定制的HACON系统?
未来的方向将集中在更智能的代理、更直观的用户界面、更深层次的协作学习机制,以及将HACON应用于更广泛的商业和社会场景。
结语
人机协作谈判代表了人机协作的未来趋势。它通过精心设计的架构,将人类的智慧与AI的效率融为一体,为复杂、动态的谈判场景提供了强大的解决方案。这种共生关系,将使我们在面对未来的挑战时,拥有更强的适应性和竞争力。