人工接管协议
检测接管触发条件,并顺畅地转交给人工客服
人工接管协议 是 CoddyKit 上的免费 AI Agents 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Agents 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Agents 课程共包含 4 节课。
代理何时应进行转交?
并非每次对话都应由人工智能代理从头到尾处理。了解何时进行转交与了解如何回答同样重要。常见触发条件包括:
- 客户明确要求人工客服
- 检测到愤怒或 distress 情绪
- 情况复杂或含义不明确,超出代理的范围
- 涉及法律、安全或合规敏感性
检测转交触发条件
使用 LLM 分类器实时检测转交触发条件。请在代理的每一轮交互中都进行检查,而不只是检查第一条消息。
import openai, json
client = openai.OpenAI(api_key='YOUR_OPENAI_KEY')
def should_handoff(message: str, history: list[dict]) -> dict:
context = '\n'.join(f"{m['role']}: {m['content']}" for m in history[-4:])
prompt = (
f'Conversation context:\n{context}\n'
f'Latest message: "{message}"\n'
f'Should this be handed to a human agent? Reasons: '
f'angry_customer, explicit_human_request, complex_issue, legal_risk, other.\n'
f'JSON: {{"handoff": true/false, "reason": "..."}}'
)
resp = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}],
response_format={'type': 'json_object'}
)
return json.loads(resp.choices[0].message.content)热转接与冷转接
转交有两种方式:
- 热转接:代理向人工客服介绍自己和客户,概述对话内容,并等待人工客服确认后再退出。
- 冷转接:系统附带对话摘要转移对话,代理随即断开连接。
热转接可以减少客户的不满,但要求人工客服能够实时接待。
生成对话摘要
转交之前,代理会生成一份结构化对话摘要。人工客服可以将此摘要作为上下文查看,从而无需客户重复说明情况。
def generate_handoff_summary(history: list[dict]) -> str:
transcript = '\n'.join(
f"{m['role'].upper()}: {m['content']}" for m in history
)
prompt = (
f'Summarize this support conversation for a human agent.\n'
f'Include: customer issue, what was tried, current status, and urgency level.\n'
f'Be brief (3-5 sentences).\n\n'
f'TRANSCRIPT:\n{transcript}'
)
resp = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}]
)
return resp.choices[0].message.content
summary = generate_handoff_summary([
{'role': 'user', 'content': 'My account was charged twice for January'},
{'role': 'assistant', 'content': 'I can look into that for you...'}
])
print(summary)转交时创建 Zendesk 工单
进行转交时,请创建一张包含摘要和完整对话记录的 Zendesk 工单。人工客服打开工单后即可立即了解上下文。
import requests
ZENDESK_DOMAIN = 'yourcompany.zendesk.com'
ZENDESK_TOKEN = 'YOUR_ZENDESK_API_TOKEN'
ZENDESK_EMAIL = 'agent@yourcompany.com'
def create_zendesk_ticket(customer_email: str, subject: str,
summary: str, transcript: str) -> str:
payload = {
'ticket': {
'subject': subject,
'comment': {'body': f'AI Agent Summary:\n{summary}\n\nFull Transcript:\n{transcript}'},
'requester': {'email': customer_email},
'tags': ['ai_handoff'],
'priority': 'high'
}
}
resp = requests.post(
f'https://{ZENDESK_DOMAIN}/api/v2/tickets.json',
json=payload,
auth=(f'{ZENDESK_EMAIL}/token', ZENDESK_TOKEN)
)
resp.raise_for_status()
return str(resp.json()['ticket']['id'])转交 Intercom 对话
使用 Intercom 时,请通过 Intercom API 将对话分配给特定团队或代理来完成转交。人工客服会收到通知,并可以直接继续处理该对话。
import requests
INTERCOM_TOKEN = 'YOUR_INTERCOM_ACCESS_TOKEN'
def handoff_to_intercom_team(conversation_id: str, team_id: str,
note: str) -> bool:
headers = {
'Authorization': f'Bearer {INTERCOM_TOKEN}',
'Content-Type': 'application/json'
}
# Add a note with the AI summary
requests.post(
f'https://api.intercom.io/conversations/{conversation_id}/parts',
headers=headers,
json={'type': 'note', 'body': note}
)
# Assign to human team
resp = requests.put(
f'https://api.intercom.io/conversations/{conversation_id}/parts',
headers=headers,
json={'type': 'assignment', 'assignee_id': team_id,
'message_type': 'assignment'}
)
return resp.status_code == 200发送给客户的转交消息
客户在转交过程中收到的消息非常重要。消息应确认正在进行转交,说明预计等待时间,并表达对该问题的重视。
def generate_handoff_message(reason: str, wait_minutes: int = 5) -> str:
messages = {
'explicit_human_request':
f'Of course! I am connecting you with a human agent right now. '
f'Estimated wait: {wait_minutes} minutes. Your conversation history '
f'has been shared so you will not need to repeat anything.',
'angry_customer':
f'I completely understand your frustration. Let me get a senior '
f'team member on the line immediately. Wait: ~{wait_minutes} min.',
'complex_issue':
f'This situation needs specialist attention. I am escalating now '
f'and sharing all the context we have discussed. Wait: ~{wait_minutes} min.',
'legal_risk':
f'This matter requires our compliance team. Connecting you now.'
}
return messages.get(reason, f'Connecting you with a human agent. ~{wait_minutes} min wait.')
if __name__ == '__main__':
print(generate_handoff_message('angry_customer', wait_minutes=3))
print(generate_handoff_message('explicit_human_request'))
无人工客服可用时进行排队
在非工作时间或业务量较大时,可能无法立即提供人工客服。请将转交请求加入队列,向客户发送包含参考编号的确认消息,并通过 Slack 或 PagerDuty 通知值班人员。
import requests
SLACK_WEBHOOK = 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL'
def notify_on_call(ticket_id: str, summary: str, priority: str):
payload = {
'text': f'*New AI Handoff* [{priority.upper()}]',
'attachments': [{
'color': '#ff0000' if priority == 'high' else '#ffcc00',
'fields': [
{'title': 'Ticket ID', 'value': ticket_id, 'short': True},
{'title': 'Summary', 'value': summary[:500]}
]
}]
}
requests.post(SLACK_WEBHOOK, json=payload)转交后的代理行为
触发转交后,代理应停止尝试解决问题。代理仍可以回答事实性问题(例如订单状态和政策链接),但不应做出承诺或决定。
def agent_post_handoff_response(message: str, handoff_complete: bool) -> str:
if not handoff_complete:
return 'Connecting you now...'
# Still answer simple factual questions
simple_keywords = ['status', 'where', 'when', 'policy', 'link']
if any(kw in message.lower() for kw in simple_keywords):
return 'I can help with that while you wait for the agent.'
# Defer everything else
return (
'Your case has been assigned to a specialist. '
'They will respond shortly. I will step back to avoid confusion.'
)
if __name__ == '__main__':
print(agent_post_handoff_response('Where is my order?', handoff_complete=True))
print(agent_post_handoff_response('I want a refund now', handoff_complete=True))
跟踪转交指标
请衡量转交率、原因分布和转交后的解决时间。某个特定意图的转交率较高,说明代理需要加强对该主题的处理能力。
from collections import Counter
import json
handoff_log = [] # In production: a database table
def record_handoff(session_id: str, reason: str, turn_number: int):
handoff_log.append({
'session_id': session_id,
'reason': reason,
'turns_before_handoff': turn_number
})
def handoff_analytics() -> dict:
reasons = Counter(h['reason'] for h in handoff_log)
avg_turns = sum(h['turns_before_handoff'] for h in handoff_log) / max(len(handoff_log), 1)
return {
'total_handoffs': len(handoff_log),
'reason_breakdown': dict(reasons),
'avg_turns_before_handoff': round(avg_turns, 1)
}
if __name__ == '__main__':
record_handoff('s1', 'angry_customer', 4)
record_handoff('s2', 'complex_issue', 7)
record_handoff('s3', 'angry_customer', 2)
stats = handoff_analytics()
print(f"Total handoffs: {stats['total_handoffs']}")
print(f"Reasons: {stats['reason_breakdown']}")
print(f"Avg turns before handoff: {stats['avg_turns_before_handoff']}")
完整的转交编排
将所有步骤组合到 execute_handoff() 函数中,代理检测到触发条件后调用一次该函数。
def execute_handoff(session: dict, reason: str) -> str:
# 1. Generate summary
summary = generate_handoff_summary(session['history'])
transcript = '\n'.join(
f"{m['role']}: {m['content']}" for m in session['history']
)
# 2. Create ticket
ticket_id = create_zendesk_ticket(
session['customer_email'],
f'AI Handoff: {reason}',
summary,
transcript
)
# 3. Notify on-call team
notify_on_call(ticket_id, summary, priority='high')
# 4. Record metrics
record_handoff(session['id'], reason, len(session['history']))
# 5. Return customer-facing message
wait = 5 # fetch from queue depth in production
return generate_handoff_message(reason, wait)热转接和冷转接之间的关键区别是什么?
选择合适的转交方式会影响客户体验和运营复杂度。了解二者的区别有助于您实现适当的处理协议。
人工转交协议回顾
有效的转交需要:检测触发条件(愤怒、明确请求和复杂情况)、为人工客服生成摘要、在 Zendesk 或 Intercom 中创建包含完整对话记录的工单、通知值班人员,以及向客户提供清晰的预期说明。
转交后,代理应退居幕后,将所有决定交由人工客服处理。
常见问题解答
「人工接管协议」课时是免费的吗?
是的 — 「人工接管协议」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Agents 课程的其余内容,请升级到 CoddyKit PRO。 AI Agents 课程共包含 4 节课。
「人工接管协议」这节课中我会学到什么?
检测接管触发条件,并顺畅地转交给人工客服 你通过在浏览器中直接运行的动手代码来练习 AI Agents,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 AI Agents 需要有经验吗?
无需任何先前经验。CoddyKit 上的 AI Agents 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「人工接管协议」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 AI Agents 课中编写并运行代码吗?
能。每节 AI Agents 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。