Files
ailine/backend/app/agent_subgraphs/contact/nodes.py
root 37ceb6c260
Some checks failed
构建并部署 AI Agent 服务 / deploy (push) Has been cancelled
重构:通讯录子图支持 async 和 API 注入工厂模式
2026-04-27 17:34:44 +08:00

279 lines
9.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
通讯录子图节点 - 使用公共工具版本
Contact Subgraph Nodes - Using Common Tools
支持 async 和 API 注入
"""
from typing import Dict, Any
from datetime import datetime
# 公共工具
from ..common import MarkdownFormatter
from .state import ContactState, ContactAction, Contact, Email
from .api_client import ContactAPIClient
# 模拟联系人数据库(临时存储,保留作为备选)
CONTACT_DB = {}
def create_contact_nodes(contact_api: ContactAPIClient):
"""
创建通讯录子图节点工厂函数
Args:
contact_api: 已初始化的 ContactAPIClient支持真实数据库或模拟模式
Returns:
节点函数字典
"""
async def parse_intent(state: ContactState) -> ContactState:
"""
解析用户意图节点
"""
query_lower = state.user_query.lower()
if any(keyword in query_lower for keyword in ["添加", "add", "新建", "save"]):
state.action = ContactAction.CONTACT_ADD
elif any(keyword in query_lower for keyword in ["联系人", "contact", "list"]):
state.action = ContactAction.CONTACT_LIST
state.action_params = {"query": state.user_query}
elif any(keyword in query_lower for keyword in ["邮件", "email", "inbox"]):
state.action = ContactAction.EMAIL_LIST
elif any(keyword in query_lower for keyword in ["发送邮件", "send email", "发邮件"]):
state.action = ContactAction.EMAIL_SEND
else:
state.action = ContactAction.SNIFF_CONTACTS
return state
async def list_contacts(state: ContactState) -> ContactState:
"""
列出联系人节点
"""
state.current_phase = "executing"
# 使用 API 客户端async
contacts = await contact_api.list_contacts(state.user_id)
state.contacts = contacts
return state
async def add_contact(state: ContactState) -> ContactState:
"""
添加联系人节点
"""
state.current_phase = "executing"
# 使用 API 客户端(简化添加,实际项目应解析用户输入)
new_contact = Contact(
id=str(len(CONTACT_DB) + 1),
name="新联系人",
email="new@example.com",
phone="13800000000",
created_at=datetime.now().isoformat()
)
# 保存到数据库
await contact_api.add_contact(state.user_id, new_contact)
state.current_contact = new_contact
return state
async def list_emails(state: ContactState) -> ContactState:
"""
列出邮件节点
"""
state.current_phase = "executing"
# 使用 API 客户端async
emails = await contact_api.list_emails(state.user_id)
state.emails = emails
return state
async def generate_email_draft(state: ContactState) -> ContactState:
"""
生成邮件草稿节点
"""
state.current_phase = "executing"
# 使用 API 客户端async
draft = await contact_api.generate_email_draft(state.user_query)
state.draft_recipient = draft.get("recipient", "recipient@example.com")
state.draft_subject = draft.get("subject", "邮件主题")
state.draft_body = draft.get("body", "邮件正文")
return state
async def sniff_contacts(state: ContactState) -> ContactState:
"""
嗅探联系人节点
"""
state.current_phase = "executing"
# 使用 API 客户端async
contacts = await contact_api.sniff_contacts(state.user_query)
state.sniffed_contacts = contacts
return state
async def format_result(state: ContactState) -> ContactState:
"""
格式化结果节点(使用公共工具)
"""
state.current_phase = "formatting"
md = MarkdownFormatter()
output_lines = []
output_lines.append("┌───────────────────────────────────┐")
output_lines.append("│ 📇 通讯录助手 │")
output_lines.append("└───────────────────────────────────┘")
output_lines.append("")
if state.action == ContactAction.CONTACT_LIST and state.contacts:
output_lines.append(md.heading("📇 联系人列表", 2))
output_lines.append("")
contact_data = [
{"姓名": c.name, "邮箱": c.email, "电话": c.phone or "-"}
for c in state.contacts
]
output_lines.append(md.table(contact_data))
elif state.action == ContactAction.EMAIL_LIST and state.emails:
output_lines.append(md.heading("📬 邮件列表", 2))
output_lines.append("")
# 兼容两种 date 格式
email_data = []
for e in state.emails:
date_str = e.date
if hasattr(e, 'received_at') and e.received_at:
try:
date_str = e.received_at.strftime('%Y-%m-%d %H:%M')
except:
pass
email_data.append({
"发件人": e.sender,
"主题": e.subject,
"时间": date_str
})
output_lines.append(md.table(email_data))
elif state.action == ContactAction.EMAIL_SEND and state.draft_subject:
output_lines.append(md.heading("📝 邮件草稿", 2))
output_lines.append("")
output_lines.append(f"**收件人**: {state.draft_recipient}")
output_lines.append(f"**主题**: {state.draft_subject}")
output_lines.append("")
output_lines.append(md.quote(state.draft_body))
elif state.action == ContactAction.SNIFF_CONTACTS and state.sniffed_contacts:
output_lines.append(md.heading("🔍 发现的联系人", 2))
output_lines.append("")
contact_data = [
{"姓名": c.name, "邮箱": c.email}
for c in state.sniffed_contacts
]
output_lines.append(md.table(contact_data))
else:
output_lines.append(md.heading("✨ 操作完成", 2))
output_lines.append("您的请求已处理。")
# 页脚提示
output_lines.append("")
output_lines.append("---")
output_lines.append("💡 提示:您可以继续查询联系人、查看邮件,或者生成邮件草稿!")
state.final_result = "\n".join(output_lines)
state.success = True
state.current_phase = "completed"
return state
async def human_review(state: ContactState) -> ContactState:
"""
人工审核节点(用于邮件草稿)
"""
state.current_phase = "reviewing"
# 标记需要审核,等待用户决定
state.needs_approval = True
return state
async def send_email(state: ContactState) -> ContactState:
"""
发送邮件节点
"""
state.current_phase = "executing"
# 使用 API 客户端发送邮件async
success = await contact_api.send_email(
state.user_id,
state.draft_recipient,
state.draft_subject,
state.draft_body
)
state.success = success
return state
def should_continue(state: ContactState) -> str:
"""
条件路由函数:根据 action 和状态决定下一个节点
"""
# 如果是从 human_review 来的,根据审核状态决定
if state.current_phase == "reviewing":
if state.needs_approval:
# 这里会等待用户操作,实际运行时通过 checkpointer 或后端 API 处理
return "format_result"
else:
return "send_email"
# 普通路由
action = state.action
if action == ContactAction.CONTACT_LIST:
return "list_contacts"
elif action == ContactAction.CONTACT_ADD:
return "add_contact"
elif action == ContactAction.EMAIL_LIST:
return "list_emails"
elif action == ContactAction.EMAIL_SEND:
return "generate_email_draft"
elif action == ContactAction.SNIFF_CONTACTS:
return "sniff_contacts"
else:
return "format_result"
# 返回节点字典
return {
"parse_intent": parse_intent,
"list_contacts": list_contacts,
"add_contact": add_contact,
"list_emails": list_emails,
"generate_email_draft": generate_email_draft,
"sniff_contacts": sniff_contacts,
"format_result": format_result,
"human_review": human_review,
"send_email": send_email,
"should_continue": should_continue
}
# ========== 向后兼容的全局版本(使用模拟 API ==========
from .api_client import contact_api as _default_contact_api
# 创建默认节点(用模拟 API保持向后兼容
_default_nodes = create_contact_nodes(_default_contact_api)
# 导出默认节点
parse_intent = _default_nodes["parse_intent"]
list_contacts = _default_nodes["list_contacts"]
add_contact = _default_nodes["add_contact"]
list_emails = _default_nodes["list_emails"]
generate_email_draft = _default_nodes["generate_email_draft"]
sniff_contacts = _default_nodes["sniff_contacts"]
format_result = _default_nodes["format_result"]
human_review = _default_nodes["human_review"]
send_email = _default_nodes["send_email"]
should_continue = _default_nodes["should_continue"]