feat: 完善词典子图,添加API调用和前端格式化工具
Some checks failed
构建并部署 AI Agent 服务 / deploy (push) Failing after 6m5s
Some checks failed
构建并部署 AI Agent 服务 / deploy (push) Failing after 6m5s
- 完善词典子图:添加生词本功能 - 创建API调用工具:dictionary_api - 添加前端格式化展示工具:result_formatter.py - 创建通讯录和资讯子图的基本结构 - 更新主图状态结构,添加MainGraphState - 添加subgraph_builder.py用于子图集成
This commit is contained in:
47
backend/app/agent_subgraphs/contact/__init__.py
Normal file
47
backend/app/agent_subgraphs/contact/__init__.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
通讯录子图
|
||||
Contact Subgraph Module
|
||||
"""
|
||||
|
||||
from .state import (
|
||||
ContactState,
|
||||
Contact,
|
||||
Email,
|
||||
ContactAction
|
||||
)
|
||||
from .graph import build_contact_subgraph
|
||||
from .nodes import (
|
||||
parse_intent,
|
||||
list_contacts,
|
||||
add_contact,
|
||||
list_emails,
|
||||
generate_email_draft,
|
||||
human_review,
|
||||
send_email,
|
||||
sniff_contacts,
|
||||
format_result,
|
||||
should_continue
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
# State
|
||||
"ContactState",
|
||||
"Contact",
|
||||
"Email",
|
||||
"ContactAction",
|
||||
|
||||
# Graph
|
||||
"build_contact_subgraph",
|
||||
|
||||
# Nodes
|
||||
"parse_intent",
|
||||
"list_contacts",
|
||||
"add_contact",
|
||||
"list_emails",
|
||||
"generate_email_draft",
|
||||
"human_review",
|
||||
"send_email",
|
||||
"sniff_contacts",
|
||||
"format_result",
|
||||
"should_continue"
|
||||
]
|
||||
86
backend/app/agent_subgraphs/contact/graph.py
Normal file
86
backend/app/agent_subgraphs/contact/graph.py
Normal file
@@ -0,0 +1,86 @@
|
||||
"""
|
||||
通讯录子图构建器
|
||||
Contact Subgraph Builder
|
||||
"""
|
||||
|
||||
from langgraph.graph import StateGraph, START, END
|
||||
|
||||
from .state import ContactState
|
||||
from .nodes import (
|
||||
parse_intent,
|
||||
list_contacts,
|
||||
add_contact,
|
||||
list_emails,
|
||||
generate_email_draft,
|
||||
human_review,
|
||||
send_email,
|
||||
sniff_contacts,
|
||||
format_result,
|
||||
should_continue
|
||||
)
|
||||
|
||||
|
||||
def build_contact_subgraph() -> StateGraph:
|
||||
"""
|
||||
构建通讯录子图
|
||||
|
||||
Returns:
|
||||
配置好的 StateGraph
|
||||
"""
|
||||
# 创建图
|
||||
graph = StateGraph(ContactState)
|
||||
|
||||
# 添加节点
|
||||
graph.add_node("parse_intent", parse_intent)
|
||||
graph.add_node("list_contacts", list_contacts)
|
||||
graph.add_node("add_contact", add_contact)
|
||||
graph.add_node("list_emails", list_emails)
|
||||
graph.add_node("generate_email_draft", generate_email_draft)
|
||||
graph.add_node("human_review", human_review)
|
||||
graph.add_node("send_email", send_email)
|
||||
graph.add_node("sniff_contacts", sniff_contacts)
|
||||
graph.add_node("format_result", format_result)
|
||||
|
||||
# 添加边
|
||||
# 从START开始
|
||||
graph.add_edge(START, "parse_intent")
|
||||
|
||||
# 从parse_intent根据条件路由
|
||||
graph.add_conditional_edges(
|
||||
"parse_intent",
|
||||
should_continue,
|
||||
{
|
||||
"list_contacts": "list_contacts",
|
||||
"add_contact": "add_contact",
|
||||
"list_emails": "list_emails",
|
||||
"generate_email_draft": "generate_email_draft",
|
||||
"sniff_contacts": "sniff_contacts",
|
||||
}
|
||||
)
|
||||
|
||||
# 从各个操作节点到format_result
|
||||
graph.add_edge("list_contacts", "format_result")
|
||||
graph.add_edge("add_contact", "format_result")
|
||||
graph.add_edge("list_emails", "format_result")
|
||||
graph.add_edge("sniff_contacts", "format_result")
|
||||
|
||||
# 邮件发送的特殊流程
|
||||
graph.add_edge("generate_email_draft", "human_review")
|
||||
|
||||
# 从human_review根据条件路由
|
||||
graph.add_conditional_edges(
|
||||
"human_review",
|
||||
should_continue,
|
||||
{
|
||||
"send_email": "send_email",
|
||||
"format_result": "format_result",
|
||||
}
|
||||
)
|
||||
|
||||
# 发送邮件后到格式化
|
||||
graph.add_edge("send_email", "format_result")
|
||||
|
||||
# 最终到END
|
||||
graph.add_edge("format_result", END)
|
||||
|
||||
return graph
|
||||
217
backend/app/agent_subgraphs/contact/nodes.py
Normal file
217
backend/app/agent_subgraphs/contact/nodes.py
Normal file
@@ -0,0 +1,217 @@
|
||||
"""
|
||||
通讯录子图节点
|
||||
Contact Subgraph Nodes
|
||||
"""
|
||||
|
||||
from typing import Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
from .state import ContactState, ContactAction, Contact, Email
|
||||
|
||||
|
||||
def parse_intent(state: ContactState) -> ContactState:
|
||||
"""
|
||||
解析用户意图节点
|
||||
确定用户想做什么操作
|
||||
"""
|
||||
state.current_phase = "intent_parsing"
|
||||
|
||||
query_lower = state.user_query.lower()
|
||||
|
||||
# 简单的关键词匹配(真实场景应该用LLM)
|
||||
if 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 ["添加", "add", "新建", "save"]):
|
||||
state.action = ContactAction.CONTACT_ADD
|
||||
# TODO: 提取联系人信息
|
||||
|
||||
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
|
||||
|
||||
|
||||
def list_contacts(state: ContactState) -> ContactState:
|
||||
"""
|
||||
列出联系人节点
|
||||
"""
|
||||
state.current_phase = "listing_contacts"
|
||||
|
||||
# TODO: 从数据库查询
|
||||
# 暂时返回空列表
|
||||
state.contacts = []
|
||||
state.success = True
|
||||
state.final_result = "暂无联系人"
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def add_contact(state: ContactState) -> ContactState:
|
||||
"""
|
||||
添加联系人节点
|
||||
"""
|
||||
state.current_phase = "adding_contact"
|
||||
|
||||
# TODO: 实现添加联系人逻辑
|
||||
state.success = True
|
||||
state.final_result = "联系人添加成功(待实现)"
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def list_emails(state: ContactState) -> ContactState:
|
||||
"""
|
||||
列出邮件节点
|
||||
"""
|
||||
state.current_phase = "listing_emails"
|
||||
|
||||
# TODO: 从IMAP查询
|
||||
state.emails = []
|
||||
state.success = True
|
||||
state.final_result = "暂无邮件"
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def generate_email_draft(state: ContactState) -> ContactState:
|
||||
"""
|
||||
生成邮件草稿节点
|
||||
"""
|
||||
state.current_phase = "generating_draft"
|
||||
|
||||
# TODO: 使用LLM生成邮件草稿
|
||||
state.draft_subject = "邮件主题"
|
||||
state.draft_recipient = "recipient@example.com"
|
||||
state.draft_body = "这是邮件内容..."
|
||||
|
||||
# 进入人工审核状态
|
||||
state.pending_review = True
|
||||
state.review_type = "email_send"
|
||||
state.review_prompt = "请确认是否发送此邮件"
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def human_review(state: ContactState) -> ContactState:
|
||||
"""
|
||||
人工审核节点
|
||||
这里会让用户确认/修改
|
||||
"""
|
||||
state.current_phase = "reviewing"
|
||||
|
||||
# 注意:真实的LangGraph会在这里使用interrupt()暂停
|
||||
# 这里我们只设置状态,让外层处理
|
||||
if state.review_approved is True:
|
||||
state.pending_review = False
|
||||
elif state.review_approved is False:
|
||||
state.pending_review = False
|
||||
state.error_message = "发送已取消"
|
||||
state.success = False
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def send_email(state: ContactState) -> ContactState:
|
||||
"""
|
||||
发送邮件节点
|
||||
"""
|
||||
state.current_phase = "sending_email"
|
||||
|
||||
# TODO: 使用SMTP发送邮件
|
||||
state.success = True
|
||||
state.final_result = "邮件发送成功(待实现)"
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def sniff_contacts(state: ContactState) -> ContactState:
|
||||
"""
|
||||
智能嗅探节点
|
||||
从对话中提取可能的联系人信息
|
||||
"""
|
||||
state.current_phase = "sniffing"
|
||||
|
||||
# TODO: 实现智能嗅探
|
||||
state.success = True
|
||||
state.final_result = "智能嗅探完成(待实现)"
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def format_result(state: ContactState) -> ContactState:
|
||||
"""
|
||||
格式化结果节点
|
||||
"""
|
||||
state.current_phase = "formatting"
|
||||
|
||||
# 根据不同action生成不同的格式化输出
|
||||
if state.action == ContactAction.CONTACT_LIST:
|
||||
if state.contacts:
|
||||
result = "联系人列表:\n"
|
||||
for i, contact in enumerate(state.contacts, 1):
|
||||
result += f"{i}. {contact.name}"
|
||||
if contact.phone:
|
||||
result += f" - {contact.phone}"
|
||||
if contact.email:
|
||||
result += f" ({contact.email})"
|
||||
result += "\n"
|
||||
else:
|
||||
result = "暂无联系人"
|
||||
state.final_result = result
|
||||
|
||||
elif state.action == ContactAction.EMAIL_LIST:
|
||||
if state.emails:
|
||||
result = "邮件列表:\n"
|
||||
for i, email in enumerate(state.emails[:10], 1):
|
||||
result += f"{i}. {email.subject} - {email.sender}\n"
|
||||
else:
|
||||
result = "暂无邮件"
|
||||
state.final_result = result
|
||||
|
||||
else:
|
||||
if not state.final_result:
|
||||
state.final_result = "操作完成"
|
||||
|
||||
state.current_phase = "done"
|
||||
return state
|
||||
|
||||
|
||||
def should_continue(state: ContactState) -> str:
|
||||
"""
|
||||
条件路由:决定下一步该做什么
|
||||
"""
|
||||
if state.error_message:
|
||||
return "finalize"
|
||||
|
||||
# 如果在审核中,等待
|
||||
if state.pending_review:
|
||||
return "human_review"
|
||||
|
||||
# 根据action路由
|
||||
if state.action == ContactAction.NONE:
|
||||
return "parse_intent"
|
||||
elif state.action == ContactAction.CONTACT_LIST:
|
||||
return "list_contacts"
|
||||
elif state.action == ContactAction.CONTACT_ADD:
|
||||
return "add_contact"
|
||||
elif state.action == ContactAction.EMAIL_LIST:
|
||||
return "list_emails"
|
||||
elif state.action == ContactAction.EMAIL_SEND:
|
||||
if state.pending_review:
|
||||
return "human_review"
|
||||
elif state.draft_subject:
|
||||
return "send_email"
|
||||
else:
|
||||
return "generate_email_draft"
|
||||
elif state.action == ContactAction.SNIFF_CONTACTS:
|
||||
return "sniff_contacts"
|
||||
else:
|
||||
return "format_result"
|
||||
104
backend/app/agent_subgraphs/contact/state.py
Normal file
104
backend/app/agent_subgraphs/contact/state.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
通讯录子图状态定义
|
||||
Contact Subgraph State Definition
|
||||
"""
|
||||
|
||||
from enum import Enum, auto
|
||||
from typing import Optional, Dict, List, Any
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
class ContactAction(Enum):
|
||||
"""通讯录操作类型"""
|
||||
NONE = auto()
|
||||
CONTACT_LIST = auto() # 联系人列表
|
||||
CONTACT_ADD = auto() # 添加联系人
|
||||
CONTACT_UPDATE = auto() # 更新联系人
|
||||
CONTACT_DELETE = auto() # 删除联系人
|
||||
EMAIL_LIST = auto() # 邮件列表
|
||||
EMAIL_READ = auto() # 读取邮件
|
||||
EMAIL_SEND = auto() # 发送邮件
|
||||
SNIFF_CONTACTS = auto() # 智能嗅探
|
||||
|
||||
|
||||
@dataclass
|
||||
class Contact:
|
||||
"""联系人数据结构"""
|
||||
id: Optional[str] = None
|
||||
name: str = ""
|
||||
phone: str = ""
|
||||
email: str = ""
|
||||
company: str = ""
|
||||
position: str = ""
|
||||
notes: str = ""
|
||||
created_at: Optional[str] = None
|
||||
updated_at: Optional[str] = None
|
||||
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Email:
|
||||
"""邮件数据结构"""
|
||||
id: Optional[str] = None
|
||||
subject: str = ""
|
||||
sender: str = ""
|
||||
recipients: List[str] = field(default_factory=list)
|
||||
date: Optional[str] = None
|
||||
body: str = ""
|
||||
is_read: bool = False
|
||||
mailbox: str = ""
|
||||
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ContactState:
|
||||
"""通讯录子图状态"""
|
||||
# ========== 输入 ==========
|
||||
user_query: str = "" # 用户查询
|
||||
user_id: str = "" # 用户ID
|
||||
|
||||
# 操作控制
|
||||
action: ContactAction = ContactAction.NONE
|
||||
action_params: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
# ========== 执行过程 ==========
|
||||
# 当前阶段
|
||||
current_phase: str = "init" # init, processing, reviewing, done
|
||||
|
||||
# 联系人相关
|
||||
contacts: List[Contact] = field(default_factory=list)
|
||||
current_contact: Optional[Contact] = None
|
||||
|
||||
# 邮件相关
|
||||
emails: List[Email] = field(default_factory=list)
|
||||
current_email: Optional[Email] = None
|
||||
|
||||
# 邮件草稿(用于审核)
|
||||
draft_subject: str = ""
|
||||
draft_recipient: str = ""
|
||||
draft_body: str = ""
|
||||
|
||||
# ========== 人工审核相关 ==========
|
||||
pending_review: bool = False
|
||||
review_type: str = "" # email_send, contact_delete
|
||||
review_prompt: str = ""
|
||||
review_approved: Optional[bool] = None
|
||||
review_comment: str = ""
|
||||
review_modified_content: str = ""
|
||||
|
||||
# ========== 智能嗅探 ==========
|
||||
sniff_result: Optional[Dict[str, Any]] = None
|
||||
sniffed_contacts: List[Contact] = field(default_factory=list)
|
||||
sniff_confirmation_pending: bool = False
|
||||
|
||||
# ========== 结果 ==========
|
||||
success: bool = False
|
||||
error_message: str = ""
|
||||
final_result: str = ""
|
||||
result_data: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
# ========== 元数据 ==========
|
||||
start_time: Optional[str] = None
|
||||
end_time: Optional[str] = None
|
||||
duration: float = 0.0
|
||||
debug_info: Dict[str, Any] = field(default_factory=dict)
|
||||
Reference in New Issue
Block a user