TCC结合Saga保障任务流水线一致性,AI智能体任务实战教程
·
Agent任务流水线跨服务状态一致性与幂等性保证机制
在高并发分布式Agent系统中,任务流水线涉及多个服务的协同处理,状态一致性确保数据在分布式环境下保持正确,而幂等性保证重复请求不会产生副作用。这两者是构建可靠分布式系统的基石。
一、跨服务状态一致性保证机制
1.1 分布式事务管理策略
Agent任务流水线通常采用最终一致性而非强一致性,以平衡性能与正确性。以下是四种主流实现方案:
| 方案 | 原理 | 适用场景 | 优缺点 |
|---|---|---|---|
| TCC模式 | Try-Confirm-Cancel三阶段 | 对一致性要求高的金融、电商场景 | 业务侵入性强,性能较好,需实现幂等性 |
| Saga模式 | 长事务拆分为本地事务+补偿操作 | 长流程业务,如订单处理、客服工单 | 实现简单,但补偿逻辑复杂 |
| 本地消息表 | 本地事务+异步消息+重试 | 大多数业务场景,特别是写操作为主的系统 | 实现简单,保证最终一致性 |
| 2PC/3PC | 协调者统一管理提交/回滚 | 传统企业系统 | 强一致性,但存在阻塞问题 |
1.2 TCC模式在Agent流水线的具体实现
TCC(Try-Confirm-Cancel)模式通过业务层面的补偿机制保证一致性,特别适合Agent流水线中的资源预留场景。
# TCC模式在客服Agent流水线中的实现
import uuid
from datetime import datetime
from enum import Enum
from typing import Dict, Any, Optional
from dataclasses import dataclass
import redis
import json
class TCCState(Enum):
"""TCC事务状态"""
INITIAL = "initial"
TRYING = "trying"
CONFIRMED = "confirmed"
CANCELLED = "cancelled"
FAILED = "failed"
@dataclass
class TCCTransaction:
"""TCC事务记录"""
transaction_id: str
business_id: str # 业务ID,如session_id
state: TCCState
created_at: datetime
updated_at: datetime
participants: Dict[str, Dict] # 参与者状态
context: Dict[str, Any] # 事务上下文
class TCCCoordinator:
"""TCC协调器"""
def __init__(self, redis_client):
self.redis = redis_client
self.transaction_timeout = 300 # 5分钟超时
def begin_transaction(self, business_id: str, context: Dict) -> str:
"""开始TCC事务"""
transaction_id = f"tcc_{uuid.uuid4()}"
transaction = TCCTransaction(
transaction_id=transaction_id,
business_id=business_id,
state=TCCState.INITIAL,
created_at=datetime.utcnow(),
updated_at=datetime.utcnow(),
participants={},
context=context
)
# 保存事务状态到Redis
self._save_transaction(transaction)
# 设置超时监控
self._setup_timeout_monitor(transaction_id)
return transaction_id
def try_phase(self, transaction_id: str, participant: str,
try_func, *args, **kwargs) -> bool:
"""Try阶段:预留资源"""
# 获取事务
transaction = self._get_transaction(transaction_id)
if not transaction:
return False
# 检查事务状态
if transaction.state != TCCState.INITIAL:
raise Exception(f"Transaction {transaction_id} is in {transaction.state} state")
try:
# 执行Try操作
result = try_func(*args, **kwargs)
# 记录参与者状态
transaction.participants[participant] = {
'state': 'tried',
'try_result': result,
'tried_at': datetime.utcnow().isoformat()
}
transaction.state = TCCState.TRYING
transaction.updated_at = datetime.utcnow()
# 保存状态
self._save_transaction(transaction)
return True
except Exception as e:
# Try失败,触发Cancel
self._cancel_participant(transaction_id, participant, e)
return False
def confirm_phase(self, transaction_id: str) -> bool:
"""Confirm阶段:确认执行业务"""
transaction = self._get_transaction(transaction_id)
if not transaction:
return False
# 检查是否所有参与者都Try成功
all_tried = all(
info['state'] == 'tried'
for info in transaction.participants.values()
)
if not all_tried:
# 有参与者Try失败,触发Cancel
self.cancel_phase(transaction_id)
return False
# 执行所有参与者的Confirm操作
confirm_failures = []
for participant, info in transaction.participants.items():
try:
# 这里应该调用参与者的Confirm接口
# confirm_func = get_confirm_func(participant)
# confirm_func(info['try_result'])
info['state'] = 'confirmed'
info['confirmed_at'] = datetime.utcnow().isoformat()
except Exception as e:
confirm_failures.append((participant, str(e)))
if confirm_failures:
# Confirm失败,需要人工干预
transaction.state = TCCState.FAILED
transaction.context['confirm_failures'] = confirm_failures
else:
transaction.state = TCCState.CONFIRMED
transaction.updated_at = datetime.utcnow()
self._save_transaction(transaction)
return len(confirm_failures) == 0
def cancel_phase(self, transaction_id: str) -> bool:
"""Cancel阶段:取消预留资源"""
transaction = self._get_transaction(transaction_id)
if not transaction:
return False
# 执行所有参与者的Cancel操作
cancel_failures = []
for participant, info in transaction.participants.items():
try:
# 这里应该调用参与者的Cancel接口
# cancel_func = get_cancel_func(participant)
# cancel_func(info.get('try_result'))
info['state'] = 'cancelled'
info['cancelled_at'] = datetime.utcnow().isoformat()
except Exception as e:
cancel_failures.append((participant, str(e)))
if cancel_failures:
transaction.state = TCCState.FAILED
transaction.context['cancel_failures'] = cancel_failures
else:
transaction.state = TCCState.CANCELLED
transaction.updated_at = datetime.utcnow()
self._save_transaction(transaction)
return len(cancel_failures) == 0
def _cancel_participant(self, transaction_id: str, participant: str, error: Exception):
"""取消单个参与者"""
transaction = self._get_transaction(transaction_id)
if not transaction:
return
if participant in transaction.participants:
# 执行Cancel操作
# cancel_func = get_cancel_func(participant)
# cancel_func(transaction.participants[participant].get('try_result'))
transaction.participants[participant]['state'] = 'cancelled'
transaction.participants[participant]['error'] = str(error)
transaction.participants[participant]['cancelled_at'] = datetime.utcnow().isoformat()
self._save_transaction(transaction)
def _save_transaction(self, transaction: TCCTransaction):
"""保存事务状态"""
key = f"tcc_transaction:{transaction.transaction_id}"
value = {
'transaction_id': transaction.transaction_id,
'business_id': transaction.business_id,
'state': transaction.state.value,
'created_at': transaction.created_at.isoformat(),
'updated_at': transaction.updated_at.isoformat(),
'participants': transaction.participants,
'context': transaction.context
}
self.redis.setex(key, self.transaction_timeout, json.dumps(value))
def _get_transaction(self, transaction_id: str) -> Optional[TCCTransaction]:
"""获取事务状态"""
key = f"tcc_transaction:{transaction_id}"
data = self.redis.get(key)
if not data:
return None
data_dict = json.loads(data)
return TCCTransaction(
transaction_id=data_dict['transaction_id'],
business_id=data_dict['business_id'],
state=TCCState(data_dict['state']),
created_at=datetime.fromisoformat(data_dict['created_at']),
updated_at=datetime.fromisoformat(data_dict['updated_at']),
participants=data_dict['participants'],
context=data_dict['context']
)
def _setup_timeout_monitor(self, transaction_id: str):
"""设置事务超时监控"""
# 使用Redis的过期机制
# 当key过期时,触发回调进行Cancel操作
pass
# Agent流水线中的TCC应用示例
class AgentPipelineWithTCC:
"""使用TCC保证状态一致性的Agent流水线"""
def __init__(self):
self.redis = redis.Redis(host='localhost', port=6379, db=0)
self.tcc_coordinator = TCCCoordinator(self.redis)
# 定义流水线步骤
self.pipeline_steps = [
'intent_recognition',
'knowledge_retrieval',
'sentiment_analysis',
'response_generation'
]
def process_request(self, session_id: str, user_input: str) -> Dict:
"""处理用户请求,使用TCC保证一致性"""
# 开始TCC事务
context = {
'session_id': session_id,
'user_input': user_input,
'start_time': datetime.utcnow().isoformat()
}
transaction_id = self.tcc_coordinator.begin_transaction(session_id, context)
try:
# Try阶段:依次执行流水线步骤
for step in self.pipeline_steps:
success = self.tcc_coordinator.try_phase(
transaction_id=transaction_id,
participant=step,
try_func=self._try_step,
step_name=step,
session_id=session_id,
user_input=user_input
)
if not success:
# 任一步骤Try失败,整个事务回滚
self.tcc_coordinator.cancel_phase(transaction_id)
return {'status': 'failed', 'error': f'{step} try failed'}
# Confirm阶段:确认所有步骤
confirm_success = self.tcc_coordinator.confirm_phase(transaction_id)
if confirm_success:
return {
'status': 'success',
'transaction_id': transaction_id,
'result': self._get_final_result(session_id)
}
else:
return {
'status': 'failed',
'transaction_id': transaction_id,
'error': 'confirm failed, requires manual intervention'
}
except Exception as e:
# 异常情况,执行Cancel
self.tcc_coordinator.cancel_phase(transaction_id)
return {'status': 'error', 'error': str(e)}
def _try_step(self, step_name: str, session_id: str, user_input: str) -> Dict:
"""Try阶段的具体实现"""
# 预留资源或执行预处理
step_handlers = {
'intent_recognition': self._try_intent_recognition,
'knowledge_retrieval': self._try_knowledge_retrieval,
'sentiment_analysis': self._try_sentiment_analysis,
'response_generation': self._try_response_generation
}
handler = step_handlers.get(step_name)
if handler:
return handler(session_id, user_input)
else:
raise ValueError(f"Unknown step: {step_name}")
def _try_intent_recognition(self, session_id: str, user_input: str) -> Dict:
"""意图识别Try阶段"""
# 1. 检查资源(如模型是否可用)
# 2. 预留计算资源
# 3. 执行轻量级预处理
return {
'step': 'intent_recognition',
'status': 'tried',
'resource_reserved': True,
'preprocess_result': {
'tokens': len(user_input.split()),
'language': 'zh-CN',
'requires_deep_analysis': len(user_input) > 20
}
}
def _try_knowledge_retrieval(self, session_id: str, user_input: str) -> Dict:
"""知识检索Try阶段"""
# 1. 检查知识库连接
# 2. 预留查询配额
# 3. 构建查询索引
return {
'step': 'knowledge_retrieval',
'status': 'tried',
'index_built': True,
'quota_reserved': 5, # 预留5次查询
'cache_prepared': True
}
# ... 其他步骤的Try实现
def _get_final_result(self, session_id: str) -> Dict:
"""获取最终结果"""
# 从各步骤收集结果
return {
'response': '处理完成',
'session_id': session_id,
'completed_at': datetime.utcnow().isoformat()
}
1.3 基于Saga模式的最终一致性方案
对于长流程的Agent任务流水线,Saga模式通过补偿操作保证最终一致性,更适合客服场景中的复杂业务流程。
# Saga模式实现Agent任务流水线
from typing import List, Callable, Dict, Any, Optional
import asyncio
from dataclasses import dataclass
from enum import Enum
import json
class SagaStepStatus(Enum):
PENDING = "pending"
EXECUTING = "executing"
COMPLETED = "completed"
FAILED = "failed"
COMPENSATED = "compensated"
@dataclass
class SagaStep:
"""Saga步骤定义"""
name: str
execute_func: Callable # 执行函数
compensate_func: Callable # 补偿函数
timeout: int = 30 # 超时时间(秒)
retry_count: int = 3 # 重试次数
class SagaOrchestrator:
"""Saga编排器"""
def __init__(self):
self.steps: List[SagaStep] = []
self.execution_log = [] # 执行日志
self.state_store = {} # 状态存储
def add_step(self, step: SagaStep):
"""添加Saga步骤"""
self.steps.append(step)
async def execute(self, context: Dict) -> Dict:
"""执行Saga事务"""
execution_id = f"saga_{uuid.uuid4()}"
# 初始化执行上下文
execution_context = {
'execution_id': execution_id,
'context': context,
'start_time': datetime.utcnow().isoformat(),
'steps': []
}
completed_steps = []
failed_step = None
try:
# 顺序执行所有步骤
for i, step in enumerate(self.steps):
step_context = {
'step_name': step.name,
'step_index': i,
'start_time': datetime.utcnow().isoformat(),
'status': SagaStepStatus.EXECUTING.value
}
try:
# 执行步骤
result = await self._execute_step_with_retry(
step, context, execution_id
)
step_context.update({
'status': SagaStepStatus.COMPLETED.value,
'end_time': datetime.utcnow().isoformat(),
'result': result
})
completed_steps.append({
'step': step.name,
'result': result
})
# 更新上下文
context.update(result.get('context_updates', {}))
except Exception as e:
# 步骤执行失败
step_context.update({
'status': SagaStepStatus.FAILED.value,
'end_time': datetime.utcnow().isoformat(),
'error': str(e)
})
failed_step = (i, step, e)
break
if failed_step:
# 执行补偿操作
await self._compensate(completed_steps, context, execution_id)
execution_context.update({
'status': 'failed',
'failed_step': failed_step[0],
'error': str(failed_step[2]),
'compensated': True
})
else:
# 所有步骤成功
execution_context.update({
'status': 'completed',
'end_time': datetime.utcnow().isoformat(),
'results': completed_steps
})
except Exception as e:
# 编排器本身异常
execution_context.update({
'status': 'error',
'error': str(e)
})
# 记录执行日志
self.execution_log.append(execution_context)
return execution_context
async def _execute_step_with_retry(self, step: SagaStep,
context: Dict,
execution_id: str) -> Dict:
"""带重试的步骤执行"""
last_exception = None
for attempt in range(step.retry_count):
try:
# 设置超时
result = await asyncio.wait_for(
step.execute_func(context, execution_id),
timeout=step.timeout
)
return result
except asyncio.TimeoutError:
last_exception = TimeoutError(f"Step {step.name} timeout on attempt {attempt + 1}")
except Exception as e:
last_exception = e
# 等待后重试
if attempt < step.retry_count - 1:
await asyncio.sleep(2 ** attempt)
----
## 参考来源
- [分布式Agent系统如何保证一致性?](https://cloud.tencent.com/developer/techpedia/2493/19525)
- [分布式系统中跨服务事务一致性的实现与解决方案在分布式系统中,跨服务的事务一致性是系统设计的核心挑战之一。由于服务拆分后数](https://juejin.cn/post/7558284340574240810)
- [问智能体如何实现跨系统的事务一致性?](https://cloud.tencent.com/developer/ask/2186555)
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)