main主程序
发布于 2025-12-31
3.7 main主程序
Section titled “3.7 main主程序”- 掌握主程序中提示词的写法
- 掌握主程序中意图识别的方法
- 掌握主程序中使用streamlit描述
一、主程序介绍
Section titled “一、主程序介绍”app.py 是一个 Streamlit 前端客户端脚本,将所有的智能体和流程串联起来,完成对话流。
作用:在 SmartVoyage 项目中,前端客户端作为用户交互层,提供图形界面输入查询,展示路由结果和响应,提升用户体验,同时展示代理卡片信息。
项目中定位:客户端是用户入口,收集查询,调用路由服务器识别意图,路由到代理,显示结果。
数据流:用户输入 → 意图识别 → 代理调用 → 结果展示。
核心功能:
-
初始化网络
-
对用户的意图进行识别,并进行改写
-
处理用户查询,路由到代理,发送任务,解析结果。
-
展示聊天消息、代理卡片和页脚。
作用:主要记录在main.py文件中用到的所有提示词,方便管理。
位置:SmartVoyage/main_prompts.py
from langchain_core.prompts import ChatPromptTemplate
class SmartVoyagePrompts:
# 定义意图识别提示模板 @staticmethod def intent_prompt(): return ChatPromptTemplate.from_template("""系统提示:您是一个专业的旅行意图识别专家,基于用户查询和对话历史,识别其意图,用于调用专门的agent server来执行;为方便后续的agent server处理,可以基于对话历史对用户查询进行改写,使问题更明确。严格遵守规则:- 支持意图:['weather' (天气查询), 'flight' (机票查询), 'train' (高铁/火车票查询), 'concert' (演唱会票查询), 'order' (票务预定), 'attraction' (景点推荐)] 或其组合(如 ['weather', 'flight'])。如果意图超出范围,返回意图 'out_of_scope'。- 注意票务预定和票务查询要区分开,涉及到订票时则为order,只是查询则为flight、train或concert。- 如果意图为 'out_of_scope'时,此时不需要再进行查询改写,你可以直接根据用户问题进行回复,将回复答案写到follow_up_message中即可。- 在进行用户查询改写时,不要回答其问题,也不要修改其原意,只需要将对话历史中跟该查询相关的上下文信息取出来,然后整合到一起,使用户查询更明确即可,要仔细分析上下文信息,不要进行过度整合。如果用户查询跟对话历史无关,则输出原始查询。- 如果用户的意图很不明确或者有歧义,可以向其进行追问,将追问问题填充到follow_up_message中。- 输出严格为JSON:{{"intents": ["intent1", "intent2"], "user_queries": {{"intent1": "user_query1", "intent2": "user_query2"}}, "follow_up_message": "追问消息"}}。不要添加额外文本!
输出示例:{{"intents": ["weather"], "user_queries": {{"weather": "今天北京天气如何"}}, "follow_up_message": ""}}{{"intents": ["weather"], "user_queries": {{}}, "follow_up_message": "你问的是今天北京天气状况吗"}}{{"intents": ["weather", "flight"], "user_queries": {{"weather": "今天北京天气如何", "flight": "查询一下10月28日,从北京飞往杭州的机票"}}, "follow_up_message": ""}}{{"intents": ["out_of_scope"], "user_queries": {{}}, "follow_up_message": "你好,我是智能旅行助手,欢迎您向我提问"}}
当前日期:{current_date} (Asia/Shanghai)。对话历史:{conversation_history}用户查询:{query}""")
# 定义天气结果总结提示模板,用于LLM总结天气查询的原始响应 @staticmethod def summarize_weather_prompt(): return ChatPromptTemplate.from_template("""系统提示:您是一位专业的天气预报员,以生动、准确的风格总结天气信息。基于查询和结果:- 核心描述点:城市、日期、温度范围、天气描述、湿度、风向、降水等。- 如果结果为空或者意思为需要补充数据,则委婉提示“未找到数据,请确认城市/日期”- 语气:专业预报,如“根据最新数据,北京2025-07-31的天气预报为...”。- 保持中文,100-150字。- 如果查询无关,返回“请提供天气相关查询。”
查询:{query}结果:{raw_response}""")
# 定义票务结果总结提示模板,用于LLM总结票务查询的原始响应 @staticmethod def summarize_ticket_prompt(): return ChatPromptTemplate.from_template("""系统提示:您是一位专业的旅行顾问,以热情、精确的风格总结票务信息。基于查询和结果:- 核心描述点:出发/到达、时间、类型、价格、剩余座位等。- 如果结果为空或者意思为需要补充数据,则委婉提示“未找到数据,请确认或修改条件”- 语气:顾问式,如“为您推荐北京到上海的机票选项...”。- 保持中文,100-150字。- 如果查询无关,返回“请提供票务相关查询。”
查询:{query}结果:{raw_response}""")
# 定义景点推荐提示模板,用于LLM直接生成景点推荐内容 @staticmethod def attraction_prompt(): return ChatPromptTemplate.from_template("""系统提示:您是一位旅行专家,基于用户查询生成景点推荐。规则:- 推荐3-5个景点,包含描述、理由、注意事项。- 基于槽位:城市、偏好。- 语气:热情推荐,如“推荐您在北京探索故宫...”。- 备注:内容生成,仅供参考。- 保持中文,150-250字。
查询:{query}""")
if __name__ == '__main__': print(SmartVoyagePrompts.intent_prompt())作用:进行导包和全局变量设置,其中messages为了方便前端渲染页面使用。
位置:SmartVoyage/main.py
import asyncioimport jsonimport uuidfrom datetime import datetimeimport pytzimport refrom python_a2a import AgentNetwork, TextContent, Message, MessageRole, Taskfrom langchain_openai import ChatOpenAI
from SmartVoyage.config import Configfrom SmartVoyage.create_logger import loggerfrom SmartVoyage.main_prompts import SmartVoyagePrompts
conf = Config()
# 初始化全局变量,用于模拟会话状态 这些变量替换了Streamlit的session_statemessages = [] # 存储对话历史消息列表,每个元素为字典{"role": "user/assistant", "content": "消息内容"}agent_network = None # 代理网络实例llm = None # 大语言模型实例agent_urls = {} # 存储代理的URL信息字典conversation_history = "" # 存储整个对话历史字符串,用于意图识别
# 初始化代理网络和相关组件 此部分在脚本启动时执行一次,模拟Streamlit的初始化def initialize_system(): """ 初始化系统组件,包括代理网络、路由器、LLM和会话状态 核心逻辑:构建AgentNetwork,添加代理,创建路由器和LLM """ global agent_network, llm, agent_urls, conversation_history # 存储代理URL信息,便于查看 agent_urls = { "WeatherQueryAssistant": "http://localhost:5005", # 天气代理URL "TicketQueryAssistant": "http://localhost:5006", # 票务代理URL "TicketOrderAssistant": "http://localhost:5007" # 票务预定URL } # 创建代理网络 network = AgentNetwork(name="旅行助手网络") network.add("WeatherQueryAssistant", "http://localhost:5005") network.add("TicketQueryAssistant", "http://localhost:5006") network.add("TicketOrderAssistant", "http://localhost:5007") agent_network = network
# 加载配置并创建LLM llm = ChatOpenAI( model=conf.model_name, api_key=conf.api_key, base_url=conf.base_url, temperature=0.1 )
# 初始化对话历史为空字符串 conversation_history = ""四、意图识别agent
Section titled “四、意图识别agent”作用:在 SmartVoyage 项目中,意图识别agent作为系统的大脑,用于分析用户查询意图,选择合适的代理(如天气或票务代理),避免硬编码路由,提高系统智能性和扩展性。另外,意图识别agent还基于对话历史对用户查询进行改写,使问题更明确,方便其他agent进行处理。
# 意图识别agentdef intent_agent(user_input): global conversation_history, llm
# 创建意图识别链:提示模板 + LLM chain = SmartVoyagePrompts.intent_prompt() | llm
# 调用LLM进行意图识别 current_date = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d') # 获取当前日期(Asia/Shanghai时区) intent_response = chain.invoke( {"conversation_history": '\n'.join(conversation_history.split("\n")[-6:]), "query": user_input, "current_date": current_date}).content.strip() logger.info(f"意图识别原始响应: {intent_response}")
# 清理响应:移除可能的Markdown代码块标记 intent_response = re.sub(r'^```json\s*|\s*```$', '', intent_response).strip() logger.info(f"清理后响应: {intent_response}") intent_output = json.loads(intent_response) # 提取意图、改写问题和追问消息 intents = intent_output.get("intents", []) user_queries = intent_output.get("user_queries", {}) follow_up_message = intent_output.get("follow_up_message", "") logger.info(f"intents: {intents}||user_queries: {user_queries}||follow_up_message: {follow_up_message} ")
return intents, user_queries, follow_up_message作用:作为处理用户输入的核心函数,首先进行意图识别,然后根据不同的意图进行不同的处理,然后将返回结果进行整理。
# 处理用户输入的核心函数# 此函数模拟Streamlit的输入处理逻辑,包括意图识别、路由和响应生成def process_user_input(prompt): """ 处理用户输入:识别意图、调用代理、生成响应 核心逻辑:使用LLM进行意图识别,根据意图路由到相应代理或直接生成内容 """ global messages, conversation_history, llm # 添加用户消息到历史 messages.append({"role": "user", "content": prompt}) conversation_history += f"\nUser: {prompt}"
print("正在分析您的意图...") try: # 意图识别过程 intents, user_queries, follow_up_message = intent_agent(prompt)
# 根据意图输出生成响应 if "out_of_scope" in intents: # 如果意图超出范围,返回大模型直接回复 response = follow_up_message conversation_history += f"\nAssistant: {response}" elif follow_up_message != "": # 如果有追问消息,则直接返回 response = follow_up_message conversation_history += f"\nAssistant: {response}" # 更新历史 else: # 处理有效意图 responses = [] # 存储每个意图的响应列表 routed_agents = [] # 记录路由到的代理列表 for intent in intents: logger.info(f"处理意图:{intent}") # 根据意图确定代理名称 if intent == "weather": agent_name = "WeatherQueryAssistant" elif intent in ["flight", "train", "concert"]: agent_name = "TicketQueryAssistant" elif intent == "order": agent_name = "TicketOrderAssistant" else: agent_name = None
# 不同意图处理方式 if intent == "attraction": # 对于景点推荐,直接使用LLM生成 chain = SmartVoyagePrompts.attraction_prompt() | llm rec_response = chain.invoke({"query": prompt}).content.strip() responses.append(rec_response) elif agent_name: # 对于代理意图,则调用代理 # 1)获取问题 query_str = user_queries.get(intent, {}) logger.info(f"{agent_name} 查询:{query_str}") # 2)获取代理实例 agent = agent_network.get_agent(agent_name) # 3)构建历史对话信息+新查询,然后调用代理 chat_history = '\n'.join(conversation_history.split("\n")[-7:-1]) + f'\nUser: {query_str}' message = Message(content=TextContent(text=chat_history), role=MessageRole.USER) task = Task(id="task-" + str(uuid.uuid4()), message=message.to_dict()) raw_response = asyncio.run(agent.send_task_async(task)) logger.info(f"{agent_name} 原始响应: {raw_response}") # 记录原始响应日志 # 4)处理结果 if raw_response.status.state == 'completed': # 正常结果 agent_result = raw_response.artifacts[0]['parts'][0]['text'] else: # 异常结果 agent_result = raw_response.status.message['content']['text']
# 根据代理类型总结响应 if agent_name == "WeatherQueryAssistant": chain = SmartVoyagePrompts.summarize_weather_prompt() | llm final_response = chain.invoke({"query": query_str, "raw_response": agent_result}).content.strip() elif agent_name == "TicketQueryAssistant": chain = SmartVoyagePrompts.summarize_ticket_prompt() | llm final_response = chain.invoke({"query": query_str, "raw_response": agent_result}).content.strip() else : final_response = agent_result
# 5)添加到历史 responses.append(final_response) # 添加到响应列表 routed_agents.append(agent_name) # 记录路由代理 else: # 不支持的意图 responses.append("暂不支持此意图。")
# 组合所有响应 response = "\n\n".join(responses) if routed_agents: logger.info(f"路由到代理:{routed_agents}") conversation_history += f"\nAssistant: {response}" # 更新历史
# 输出助手响应(模拟Streamlit的显示) print(f"\n助手回复:\n{response}\n") # 打印响应 # 添加到消息历史 messages.append({"role": "assistant", "content": response})
except json.JSONDecodeError as json_err: # 处理JSON解析错误 logger.error(f"意图识别JSON解析失败") error_message = f"意图识别JSON解析失败:{str(json_err)}。请重试。" print(f"\n助手回复:\n{error_message}\n") # 打印错误 messages.append({"role": "assistant", "content": error_message}) except Exception as e: # 处理其他异常 logger.error(f"处理异常: {str(e)}") error_message = f"处理失败:{str(e)}。请重试。" print(f"\n助手回复:\n{error_message}\n") # 打印错误 messages.append({"role": "assistant", "content": error_message})六、系统启动
Section titled “六、系统启动”作用:通过while True实现多轮对话,模拟Streamlit的连续输入。
# 显示代理卡片信息# 此函数模拟Streamlit的右侧Agent Card,打印代理详情def display_agent_cards(): """ 显示所有代理的卡片信息,包括技能、描述、地址和状态 核心逻辑:遍历代理网络,获取并打印卡片内容 """ print("\n🛠️ Agent Cards:") for agent_name in agent_network.agents.keys(): # 获取代理卡片 agent_card = agent_network.get_agent_card(agent_name) agent_url = agent_urls.get(agent_name, "未知地址") print(f"\n--- Agent: {agent_name} ---") print(f"技能: {agent_card.skills}") print(f"描述: {agent_card.description}") print(f"地址: {agent_url}") print(f"状态: 在线") # 固定状态为在线
# 主函数:脚本入口# 初始化系统并进入交互循环if __name__ == "__main__": # 初始化系统 initialize_system() print("🤖 基于A2A的SmartVoyage旅行智能助手") print("欢迎体验智能对话!输入问题,按回车提交;输入'quit'退出;输入'cards'查看代理卡片。")
# 显示初始代理卡片 display_agent_cards()
# 交互循环:模拟Streamlit的连续输入 while True: # 获取用户输入 prompt = input("\n请输入您的问题: ").strip() if prompt.lower() == 'quit': print("感谢使用SmartVoyage!再见!") break elif prompt.lower() == 'cards': # 查看卡片条件 display_agent_cards() # 重新显示卡片 continue elif not prompt: # 空输入跳过 continue else: # 处理输入 process_user_input(prompt) # 调用核心处理函数
# 脚本结束时打印页脚信息 print("\n---") print("Powered by AI Agent | 基于Agent2Agent的旅行助手系统 v2.0")七、streamlit程序
Section titled “七、streamlit程序”位置:SmartVoyage/app.py
import osimport syssys.path.append(os.path.join(os.path.dirname(__file__), ".."))import asyncioimport uuidimport streamlit as stfrom python_a2a import AgentNetwork, Message, TextContent, MessageRole, Taskfrom langchain_openai import ChatOpenAIimport jsonfrom datetime import datetimeimport pytzimport re # 用于清理响应
from SmartVoyage.config import Configfrom SmartVoyage.create_logger import loggerfrom SmartVoyage.main_prompts import SmartVoyagePrompts
conf = Config()
# 设置页面配置st.set_page_config(page_title="基于A2A的SmartVoyage旅行助手系统", layout="wide", page_icon="🤖")
# 自定义 CSS 打造高端大气科技感,优化对比度st.markdown("""<style>/* 聊天消息框样式 */.stChatMessage { background-color: #2c3e50 !important; border-radius: 12px !important; padding: 15px !important; margin-bottom: 15px !important; box-shadow: 0 3px 6px rgba(0,0,0,0.2) !important;}
/* 用户消息框稍亮 */.stChatMessage.user { background-color: #34495e !important;}
/* ✅ 核心:强制所有文字变为白色(包括 markdown 内部) */.stChatMessage .stMarkdown,.stChatMessage .stMarkdown p,.stChatMessage .stMarkdown span,.stChatMessage .stMarkdown div,.stChatMessage .stMarkdown strong,.stChatMessage .stMarkdown em,.stChatMessage .stMarkdown code { color: #ffffff !important;}
/* 如果你想让 emoji 图标更亮一点 */.stChatMessage [data-testid="stChatMessageAvatarIcon"] { filter: brightness(1.2);}</style>""", unsafe_allow_html=True)
# 初始化会话状态if "messages" not in st.session_state: st.session_state.messages = []if "agent_network" not in st.session_state: # 存储代理URL信息,便于查看 st.session_state.agent_urls = { "WeatherQueryAssistant": "http://localhost:5005", "TicketQueryAssistant": "http://localhost:5006", "TicketOrderAssistant": "http://localhost:5007" } # 初始化网络 network = AgentNetwork(name="Travel Assistant Network") network.add("WeatherQueryAssistant", "http://localhost:5005") network.add("TicketQueryAssistant", "http://localhost:5006") network.add("TicketOrderAssistant", "http://localhost:5007") st.session_state.agent_network = network # 加载配置并创建LLM st.session_state.llm = ChatOpenAI( model=conf.model_name, api_key=conf.api_key, base_url=conf.base_url, temperature=0.1 ) # 存储对话历史用于意图识别 st.session_state.conversation_history = ""
# 意图识别agentdef intent_agent(user_input): # 创建意图识别链:提示模板 + LLM chain = SmartVoyagePrompts.intent_prompt() | st.session_state.llm
# 调用LLM进行意图识别 current_date = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d') # 获取当前日期(Asia/Shanghai时区) intent_response = chain.invoke( {"conversation_history": '\n'.join(st.session_state.conversation_history.split("\n")[-6:]), "query": user_input, "current_date": current_date}).content.strip() logger.info(f"意图识别原始响应: {intent_response}")
# 清理响应:移除可能的Markdown代码块标记 intent_response = re.sub(r'^```json\s*|\s*```$', '', intent_response).strip() logger.info(f"清理后响应: {intent_response}") intent_output = json.loads(intent_response) # 提取意图、改写问题和追问消息 intents = intent_output.get("intents", []) user_queries = intent_output.get("user_queries", {}) follow_up_message = intent_output.get("follow_up_message", "") logger.info(f"intents: {intents}||user_queries: {user_queries}||follow_up_message: {follow_up_message} ")
return intents, user_queries, follow_up_message
# 主界面布局st.title("🤖 基于A2A的SmartVoyage旅行智能助手")st.markdown("欢迎体验智能对话!输入问题,系统将精准识别意图并提供服务。")
# 两栏布局:左侧对话,右侧 Agent Cardcol1, col2 = st.columns([2, 1])
# 左侧对话区域with col1: st.subheader("💬 对话") # 对话历史 for message in st.session_state.messages: with st.chat_message(message["role"]): st.markdown(message["content"])
# 输入框 if prompt := st.chat_input("请输入您的问题..."): # 显示用户消息 with st.chat_message("user"): st.markdown(prompt) st.session_state.messages.append({"role": "user", "content": prompt}) st.session_state.conversation_history += f"\nUser: {prompt}"
# 获取 LLM 和当前日期 llm = st.session_state.llm current_date = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%Y-%m-%d')
# 意图识别 with st.spinner("正在分析您的意图..."): try: # 意图识别过程 intents, user_queries, follow_up_message = intent_agent(prompt)
# 根据意图输出生成响应 if "out_of_scope" in intents: # 如果意图超出范围,返回大模型直接回复 response = follow_up_message st.session_state.conversation_history += f"\nAssistant: {response}" elif follow_up_message != "": # 如果有追问消息,则直接返回 response = follow_up_message st.session_state.conversation_history += f"\nAssistant: {response}" # 更新历史 else: # 处理有效意图 responses = [] # 存储每个意图的响应列表 routed_agents = [] # 记录路由到的代理列表 for intent in intents: logger.info(f"处理意图:{intent}") # 根据意图确定代理名称 if intent == "weather": agent_name = "WeatherQueryAssistant" elif intent in ["flight", "train", "concert"]: agent_name = "TicketQueryAssistant" elif intent == "order": agent_name = "TicketOrderAssistant" else: agent_name = None
# 不同意图处理方式 if intent == "attraction": # 对于景点推荐,直接使用LLM生成 chain = SmartVoyagePrompts.attraction_prompt() | llm rec_response = chain.invoke({"query": prompt}).content.strip() responses.append(rec_response) elif agent_name: # 对于代理意图,则调用代理 # 1)获取问题 query_str = user_queries.get(intent, {}) logger.info(f"{agent_name} 查询:{query_str}") # 2)获取代理实例 agent = st.session_state.agent_network.get_agent(agent_name) # 3)构建历史对话信息+新查询,然后调用代理 chat_history = '\n'.join(st.session_state.conversation_history.split("\n")[-7:-1]) + f'\nUser: {query_str}' message = Message(content=TextContent(text=chat_history), role=MessageRole.USER) task = Task(id="task-" + str(uuid.uuid4()), message=message.to_dict()) raw_response = asyncio.run(agent.send_task_async(task)) logger.info(f"{agent_name} 原始响应: {raw_response}") # 记录原始响应日志 # 4)处理结果 if raw_response.status.state == 'completed': # 正常结果 agent_result = raw_response.artifacts[0]['parts'][0]['text'] else: # 异常结果 agent_result = raw_response.status.message['content']['text']
# 根据代理类型总结响应 if agent_name == "WeatherQueryAssistant": chain = SmartVoyagePrompts.summarize_weather_prompt() | llm final_response = chain.invoke( {"query": query_str, "raw_response": agent_result}).content.strip() elif agent_name == "TicketQueryAssistant": chain = SmartVoyagePrompts.summarize_ticket_prompt() | llm final_response = chain.invoke( {"query": query_str, "raw_response": agent_result}).content.strip() else: final_response = agent_result
# 5)添加到历史 responses.append(final_response) # 添加到响应列表 routed_agents.append(agent_name) # 记录路由代理 else: # 不支持的意图 responses.append("暂不支持此意图。")
response = "\n\n".join(responses) if routed_agents: logger.info(f"路由到代理:{routed_agents}") st.session_state.conversation_history += f"\nAssistant: {response}"
# 显示助手消息 with st.chat_message("assistant"): st.markdown(response) st.session_state.messages.append({"role": "assistant", "content": response}) except json.JSONDecodeError as json_err: logger.error(f"意图识别JSON解析失败") error_message = f"意图识别JSON解析失败:{str(json_err)}。请重试。" with st.chat_message("assistant"): st.markdown(error_message) st.session_state.messages.append({"role": "assistant", "content": error_message}) except Exception as e: logger.error(f"处理异常: {str(e)}") error_message = f"处理失败:{str(e)}。请重试。" with st.chat_message("assistant"): st.markdown(error_message) st.session_state.messages.append({"role": "assistant", "content": error_message})
# 右侧 Agent Card 区域with col2: st.subheader("🛠️ AgentCard") for agent_name in st.session_state.agent_network.agents.keys(): agent_card = st.session_state.agent_network.get_agent_card(agent_name) agent_url = st.session_state.agent_urls.get(agent_name, "未知地址") with st.expander(f"Agent: {agent_name}", expanded=False): st.markdown(f"<div class='card-title'>技能</div>", unsafe_allow_html=True) st.markdown(f"<div class='card-content'>{agent_card.skills}</div>", unsafe_allow_html=True) st.markdown(f"<div class='card-title'>描述</div>", unsafe_allow_html=True) st.markdown(f"<div class='card-content'>{agent_card.description}</div>", unsafe_allow_html=True) st.markdown(f"<div class='card-title'>地址</div>", unsafe_allow_html=True) st.markdown(f"<div class='card-content'>{agent_url}</div>", unsafe_allow_html=True) st.markdown(f"<div class='card-title'>状态</div>", unsafe_allow_html=True) st.markdown(f"<div class='card-content'>在线</div>", unsafe_allow_html=True)
# 页脚st.markdown("---")st.markdown('<div class="footer">Powered by AI Agent | 基于Agent2Agent的旅行助手系统 v2.0</div>', unsafe_allow_html=True)启动命令:
streamlit run app.py浏览器会弹出前台页面,输入相应的问题就可以了

本节主要介绍了主程序的逻辑实现。
发布于 2025-12-31