Showing
11 changed files
with
793 additions
and
248 deletions
| 1 | """ | 1 | """ |
| 2 | Streamlit Web界面 | 2 | Streamlit Web界面 |
| 3 | -为Deep Search Agent提供友好的Web界面 | 3 | +为Insight Agent提供友好的Web界面 |
| 4 | """ | 4 | """ |
| 5 | 5 | ||
| 6 | import os | 6 | import os |
| @@ -19,50 +19,21 @@ from config import DEEPSEEK_API_KEY, KIMI_API_KEY, DB_HOST, DB_USER, DB_PASSWORD | @@ -19,50 +19,21 @@ from config import DEEPSEEK_API_KEY, KIMI_API_KEY, DB_HOST, DB_USER, DB_PASSWORD | ||
| 19 | def main(): | 19 | def main(): |
| 20 | """主函数""" | 20 | """主函数""" |
| 21 | st.set_page_config( | 21 | st.set_page_config( |
| 22 | - page_title="Deep Search Agent", | ||
| 23 | - page_icon="🔍", | 22 | + page_title="Insight Agent", |
| 23 | + page_icon="", | ||
| 24 | layout="wide" | 24 | layout="wide" |
| 25 | ) | 25 | ) |
| 26 | 26 | ||
| 27 | - st.title("Insight Engine Agent") | ||
| 28 | - st.markdown("基于DeepSeek的本地舆情数据库深度分析AI代理") | 27 | + st.title("Insight Agent") |
| 28 | + st.markdown("私有舆情数据库深度分析AI代理") | ||
| 29 | 29 | ||
| 30 | - # 侧边栏配置 | ||
| 31 | - with st.sidebar: | ||
| 32 | - st.header("配置") | ||
| 33 | - | ||
| 34 | - # 模型选择 | ||
| 35 | - llm_provider = st.selectbox("LLM提供商", ["deepseek", "openai", "kimi"]) | ||
| 36 | - | ||
| 37 | - # 高级配置 | ||
| 38 | - st.subheader("高级配置") | ||
| 39 | - max_reflections = st.slider("反思次数", 1, 5, 2) | ||
| 40 | - | ||
| 41 | - # 根据选择的模型动态调整默认值 | ||
| 42 | - if llm_provider == "kimi": | ||
| 43 | - default_content_length = 500000 # Kimi支持长文本,使用更大的默认值 | ||
| 44 | - max_limit = 1000000 # 提高上限 | ||
| 45 | - st.info("💡 Kimi模型支持超长文本处理,建议使用更大的内容长度以充分利用其能力") | ||
| 46 | - else: | ||
| 47 | - default_content_length = 200000 | ||
| 48 | - max_limit = 500000 | ||
| 49 | - | ||
| 50 | - max_content_length = st.number_input("最大内容长度", 10000, max_limit, default_content_length) | ||
| 51 | - | ||
| 52 | - # 初始化所有可能的变量 | ||
| 53 | - openai_key = "" | ||
| 54 | - kimi_key = "" | ||
| 55 | - | ||
| 56 | - if llm_provider == "deepseek": | ||
| 57 | - model_name = st.selectbox("DeepSeek模型", ["deepseek-chat"]) | ||
| 58 | - elif llm_provider == "openai": | ||
| 59 | - model_name = st.selectbox("OpenAI模型", ["gpt-4o-mini", "gpt-4o"]) | ||
| 60 | - openai_key = st.text_input("OpenAI API Key", type="password", | ||
| 61 | - value="") | ||
| 62 | - else: # kimi | ||
| 63 | - model_name = st.selectbox("Kimi模型", ["kimi-k2-0711-preview"]) | ||
| 64 | - kimi_key = st.text_input("Kimi API Key", type="password", | ||
| 65 | - value="") | 30 | + # ----- 配置被硬编码 ----- |
| 31 | + # 强制使用 Kimi | ||
| 32 | + llm_provider = "kimi" | ||
| 33 | + model_name = "kimi-k2-0711-preview" | ||
| 34 | + # 默认高级配置 | ||
| 35 | + max_reflections = 2 | ||
| 36 | + max_content_length = 500000 # Kimi支持长文本 | ||
| 66 | 37 | ||
| 67 | # 主界面 | 38 | # 主界面 |
| 68 | col1, col2 = st.columns([2, 1]) | 39 | col1, col2 = st.columns([2, 1]) |
| @@ -75,20 +46,6 @@ def main(): | @@ -75,20 +46,6 @@ def main(): | ||
| 75 | height=100 | 46 | height=100 |
| 76 | ) | 47 | ) |
| 77 | 48 | ||
| 78 | - # 预设查询示例 | ||
| 79 | - st.subheader("示例查询") | ||
| 80 | - example_queries = [ | ||
| 81 | - "2025年人工智能发展趋势", | ||
| 82 | - "深度学习在医疗领域的应用", | ||
| 83 | - "区块链技术的最新发展", | ||
| 84 | - "可持续能源技术趋势", | ||
| 85 | - "量子计算的发展现状" | ||
| 86 | - ] | ||
| 87 | - | ||
| 88 | - selected_example = st.selectbox("选择示例查询", ["自定义"] + example_queries) | ||
| 89 | - if selected_example != "自定义": | ||
| 90 | - query = selected_example | ||
| 91 | - | ||
| 92 | with col2: | 49 | with col2: |
| 93 | st.header("状态信息") | 50 | st.header("状态信息") |
| 94 | if 'agent' in st.session_state and hasattr(st.session_state.agent, 'state'): | 51 | if 'agent' in st.session_state and hasattr(st.session_state.agent, 'state'): |
| @@ -100,8 +57,8 @@ def main(): | @@ -100,8 +57,8 @@ def main(): | ||
| 100 | st.info("尚未开始研究") | 57 | st.info("尚未开始研究") |
| 101 | 58 | ||
| 102 | # 执行按钮 | 59 | # 执行按钮 |
| 103 | - col1, col2, col3 = st.columns([1, 1, 1]) | ||
| 104 | - with col2: | 60 | + col1_btn, col2_btn, col3_btn = st.columns([1, 1, 1]) |
| 61 | + with col2_btn: | ||
| 105 | start_research = st.button("开始研究", type="primary", use_container_width=True) | 62 | start_research = st.button("开始研究", type="primary", use_container_width=True) |
| 106 | 63 | ||
| 107 | # 验证配置 | 64 | # 验证配置 |
| @@ -110,17 +67,12 @@ def main(): | @@ -110,17 +67,12 @@ def main(): | ||
| 110 | st.error("请输入研究查询") | 67 | st.error("请输入研究查询") |
| 111 | return | 68 | return |
| 112 | 69 | ||
| 113 | - if llm_provider == "openai" and not openai_key: | ||
| 114 | - st.error("请提供OpenAI API Key") | ||
| 115 | - return | ||
| 116 | - | ||
| 117 | - if llm_provider == "kimi" and not kimi_key and not KIMI_API_KEY: | ||
| 118 | - st.error("请提供Kimi API Key或在配置文件中设置KIMI_API_KEY") | 70 | + # 由于强制使用Kimi,只检查KIMI_API_KEY |
| 71 | + if not KIMI_API_KEY: | ||
| 72 | + st.error("请在您的配置文件(config.py)中设置KIMI_API_KEY") | ||
| 119 | return | 73 | return |
| 120 | 74 | ||
| 121 | # 自动使用配置文件中的API密钥和数据库配置 | 75 | # 自动使用配置文件中的API密钥和数据库配置 |
| 122 | - deepseek_key = DEEPSEEK_API_KEY | ||
| 123 | - kimi_key_final = kimi_key if kimi_key else KIMI_API_KEY | ||
| 124 | db_host = DB_HOST | 76 | db_host = DB_HOST |
| 125 | db_user = DB_USER | 77 | db_user = DB_USER |
| 126 | db_password = DB_PASSWORD | 78 | db_password = DB_PASSWORD |
| @@ -130,9 +82,9 @@ def main(): | @@ -130,9 +82,9 @@ def main(): | ||
| 130 | 82 | ||
| 131 | # 创建配置 | 83 | # 创建配置 |
| 132 | config = Config( | 84 | config = Config( |
| 133 | - deepseek_api_key=deepseek_key if llm_provider == "deepseek" else None, | ||
| 134 | - openai_api_key=openai_key if llm_provider == "openai" else None, | ||
| 135 | - kimi_api_key=kimi_key_final if llm_provider == "kimi" else None, | 85 | + deepseek_api_key=None, |
| 86 | + openai_api_key=None, | ||
| 87 | + kimi_api_key=KIMI_API_KEY, # 强制使用配置文件中的Kimi Key | ||
| 136 | db_host=db_host, | 88 | db_host=db_host, |
| 137 | db_user=db_user, | 89 | db_user=db_user, |
| 138 | db_password=db_password, | 90 | db_password=db_password, |
| @@ -140,9 +92,9 @@ def main(): | @@ -140,9 +92,9 @@ def main(): | ||
| 140 | db_port=db_port, | 92 | db_port=db_port, |
| 141 | db_charset=db_charset, | 93 | db_charset=db_charset, |
| 142 | default_llm_provider=llm_provider, | 94 | default_llm_provider=llm_provider, |
| 143 | - deepseek_model=model_name if llm_provider == "deepseek" else "deepseek-chat", | ||
| 144 | - openai_model=model_name if llm_provider == "openai" else "gpt-4o-mini", | ||
| 145 | - kimi_model=model_name if llm_provider == "kimi" else "kimi-k2-0711-preview", | 95 | + deepseek_model="deepseek-chat", # 保留默认值以兼容 |
| 96 | + openai_model="gpt-4o-mini", # 保留默认值以兼容 | ||
| 97 | + kimi_model=model_name, | ||
| 146 | max_reflections=max_reflections, | 98 | max_reflections=max_reflections, |
| 147 | max_content_length=max_content_length, | 99 | max_content_length=max_content_length, |
| 148 | output_dir="insight_engine_streamlit_reports" | 100 | output_dir="insight_engine_streamlit_reports" |
| @@ -174,7 +126,7 @@ def execute_research(query: str, config: Config): | @@ -174,7 +126,7 @@ def execute_research(query: str, config: Config): | ||
| 174 | # 处理段落 | 126 | # 处理段落 |
| 175 | total_paragraphs = len(agent.state.paragraphs) | 127 | total_paragraphs = len(agent.state.paragraphs) |
| 176 | for i in range(total_paragraphs): | 128 | for i in range(total_paragraphs): |
| 177 | - status_text.text(f"正在处理段落 {i+1}/{total_paragraphs}: {agent.state.paragraphs[i].title}") | 129 | + status_text.text(f"正在处理段落 {i + 1}/{total_paragraphs}: {agent.state.paragraphs[i].title}") |
| 178 | 130 | ||
| 179 | # 初始搜索和总结 | 131 | # 初始搜索和总结 |
| 180 | agent._initial_search_and_summary(i) | 132 | agent._initial_search_and_summary(i) |
| @@ -211,8 +163,8 @@ def display_results(agent: DeepSearchAgent, final_report: str): | @@ -211,8 +163,8 @@ def display_results(agent: DeepSearchAgent, final_report: str): | ||
| 211 | """显示研究结果""" | 163 | """显示研究结果""" |
| 212 | st.header("研究结果") | 164 | st.header("研究结果") |
| 213 | 165 | ||
| 214 | - # 结果标签页 | ||
| 215 | - tab1, tab2, tab3 = st.tabs(["最终报告", "详细信息", "下载"]) | 166 | + # 结果标签页(已移除下载选项) |
| 167 | + tab1, tab2 = st.tabs(["最终报告", "详细信息"]) | ||
| 216 | 168 | ||
| 217 | with tab1: | 169 | with tab1: |
| 218 | st.markdown(final_report) | 170 | st.markdown(final_report) |
| @@ -221,7 +173,7 @@ def display_results(agent: DeepSearchAgent, final_report: str): | @@ -221,7 +173,7 @@ def display_results(agent: DeepSearchAgent, final_report: str): | ||
| 221 | # 段落详情 | 173 | # 段落详情 |
| 222 | st.subheader("段落详情") | 174 | st.subheader("段落详情") |
| 223 | for i, paragraph in enumerate(agent.state.paragraphs): | 175 | for i, paragraph in enumerate(agent.state.paragraphs): |
| 224 | - with st.expander(f"段落 {i+1}: {paragraph.title}"): | 176 | + with st.expander(f"段落 {i + 1}: {paragraph.title}"): |
| 225 | st.write("**预期内容:**", paragraph.content) | 177 | st.write("**预期内容:**", paragraph.content) |
| 226 | st.write("**最终内容:**", paragraph.research.latest_summary[:300] + "..." | 178 | st.write("**最终内容:**", paragraph.research.latest_summary[:300] + "..." |
| 227 | if len(paragraph.research.latest_summary) > 300 | 179 | if len(paragraph.research.latest_summary) > 300 |
| @@ -237,34 +189,14 @@ def display_results(agent: DeepSearchAgent, final_report: str): | @@ -237,34 +189,14 @@ def display_results(agent: DeepSearchAgent, final_report: str): | ||
| 237 | 189 | ||
| 238 | if all_searches: | 190 | if all_searches: |
| 239 | for i, search in enumerate(all_searches): | 191 | for i, search in enumerate(all_searches): |
| 240 | - with st.expander(f"搜索 {i+1}: {search.query}"): | 192 | + with st.expander(f"搜索 {i + 1}: {search.query}"): |
| 241 | st.write("**URL:**", search.url) | 193 | st.write("**URL:**", search.url) |
| 242 | st.write("**标题:**", search.title) | 194 | st.write("**标题:**", search.title) |
| 243 | - st.write("**内容预览:**", search.content[:200] + "..." if len(search.content) > 200 else search.content) | 195 | + st.write("**内容预览:**", |
| 196 | + search.content[:200] + "..." if len(search.content) > 200 else search.content) | ||
| 244 | if search.score: | 197 | if search.score: |
| 245 | st.write("**相关度评分:**", search.score) | 198 | st.write("**相关度评分:**", search.score) |
| 246 | 199 | ||
| 247 | - with tab3: | ||
| 248 | - # 下载选项 | ||
| 249 | - st.subheader("下载报告") | ||
| 250 | - | ||
| 251 | - # Markdown下载 | ||
| 252 | - st.download_button( | ||
| 253 | - label="下载Markdown报告", | ||
| 254 | - data=final_report, | ||
| 255 | - file_name=f"deep_search_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md", | ||
| 256 | - mime="text/markdown" | ||
| 257 | - ) | ||
| 258 | - | ||
| 259 | - # JSON状态下载 | ||
| 260 | - state_json = agent.state.to_json() | ||
| 261 | - st.download_button( | ||
| 262 | - label="下载状态文件", | ||
| 263 | - data=state_json, | ||
| 264 | - file_name=f"deep_search_state_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json", | ||
| 265 | - mime="application/json" | ||
| 266 | - ) | ||
| 267 | - | ||
| 268 | 200 | ||
| 269 | if __name__ == "__main__": | 201 | if __name__ == "__main__": |
| 270 | main() | 202 | main() |
| 1 | """ | 1 | """ |
| 2 | Streamlit Web界面 | 2 | Streamlit Web界面 |
| 3 | -为Deep Search Agent提供友好的Web界面 | 3 | +为Media Agent提供友好的Web界面 |
| 4 | """ | 4 | """ |
| 5 | 5 | ||
| 6 | import os | 6 | import os |
| @@ -19,34 +19,21 @@ from config import DEEPSEEK_API_KEY, BOCHA_Web_Search_API_KEY, GEMINI_API_KEY | @@ -19,34 +19,21 @@ from config import DEEPSEEK_API_KEY, BOCHA_Web_Search_API_KEY, GEMINI_API_KEY | ||
| 19 | def main(): | 19 | def main(): |
| 20 | """主函数""" | 20 | """主函数""" |
| 21 | st.set_page_config( | 21 | st.set_page_config( |
| 22 | - page_title="Deep Search Agent", | ||
| 23 | - page_icon="🔍", | 22 | + page_title="Media Agent", |
| 23 | + page_icon="", | ||
| 24 | layout="wide" | 24 | layout="wide" |
| 25 | ) | 25 | ) |
| 26 | 26 | ||
| 27 | - st.title("Deep Search Agent") | ||
| 28 | - st.markdown("基于DeepSeek的无框架深度搜索AI代理") | 27 | + st.title("Media Agent") |
| 28 | + st.markdown("具备强大多模态能力的AI代理") | ||
| 29 | 29 | ||
| 30 | - # 侧边栏配置 | ||
| 31 | - with st.sidebar: | ||
| 32 | - st.header("配置") | ||
| 33 | - | ||
| 34 | - # 高级配置 | ||
| 35 | - st.subheader("高级配置") | ||
| 36 | - max_reflections = st.slider("反思次数", 1, 5, 2) | ||
| 37 | - max_content_length = st.number_input("最大内容长度", 1000, 50000, 20000) | ||
| 38 | - | ||
| 39 | - # 模型选择 | ||
| 40 | - llm_provider = st.selectbox("LLM提供商", ["deepseek", "openai", "gemini"]) | ||
| 41 | - | ||
| 42 | - openai_key = "" # 初始化变量 | ||
| 43 | - if llm_provider == "deepseek": | ||
| 44 | - model_name = st.selectbox("DeepSeek模型", ["deepseek-chat"]) | ||
| 45 | - elif llm_provider == "openai": | ||
| 46 | - model_name = st.selectbox("OpenAI模型", ["gpt-4o-mini", "gpt-4o"]) | ||
| 47 | - openai_key = st.text_input("OpenAI API Key", type="password", value="") | ||
| 48 | - else: # gemini | ||
| 49 | - model_name = st.selectbox("Gemini模型", ["gemini-2.5-pro"]) | 30 | + # ----- 配置被硬编码 ----- |
| 31 | + # 强制使用 Gemini | ||
| 32 | + llm_provider = "gemini" | ||
| 33 | + model_name = "gemini-2.5-pro" | ||
| 34 | + # 默认高级配置 | ||
| 35 | + max_reflections = 2 | ||
| 36 | + max_content_length = 20000 | ||
| 50 | 37 | ||
| 51 | # 主界面 | 38 | # 主界面 |
| 52 | col1, col2 = st.columns([2, 1]) | 39 | col1, col2 = st.columns([2, 1]) |
| @@ -59,20 +46,6 @@ def main(): | @@ -59,20 +46,6 @@ def main(): | ||
| 59 | height=100 | 46 | height=100 |
| 60 | ) | 47 | ) |
| 61 | 48 | ||
| 62 | - # 预设查询示例 | ||
| 63 | - st.subheader("示例查询") | ||
| 64 | - example_queries = [ | ||
| 65 | - "2025年人工智能发展趋势", | ||
| 66 | - "深度学习在医疗领域的应用", | ||
| 67 | - "区块链技术的最新发展", | ||
| 68 | - "可持续能源技术趋势", | ||
| 69 | - "量子计算的发展现状" | ||
| 70 | - ] | ||
| 71 | - | ||
| 72 | - selected_example = st.selectbox("选择示例查询", ["自定义"] + example_queries) | ||
| 73 | - if selected_example != "自定义": | ||
| 74 | - query = selected_example | ||
| 75 | - | ||
| 76 | with col2: | 49 | with col2: |
| 77 | st.header("状态信息") | 50 | st.header("状态信息") |
| 78 | if 'agent' in st.session_state and hasattr(st.session_state.agent, 'state'): | 51 | if 'agent' in st.session_state and hasattr(st.session_state.agent, 'state'): |
| @@ -84,8 +57,8 @@ def main(): | @@ -84,8 +57,8 @@ def main(): | ||
| 84 | st.info("尚未开始研究") | 57 | st.info("尚未开始研究") |
| 85 | 58 | ||
| 86 | # 执行按钮 | 59 | # 执行按钮 |
| 87 | - col1, col2, col3 = st.columns([1, 1, 1]) | ||
| 88 | - with col2: | 60 | + col1_btn, col2_btn, col3_btn = st.columns([1, 1, 1]) |
| 61 | + with col2_btn: | ||
| 89 | start_research = st.button("开始研究", type="primary", use_container_width=True) | 62 | start_research = st.button("开始研究", type="primary", use_container_width=True) |
| 90 | 63 | ||
| 91 | # 验证配置 | 64 | # 验证配置 |
| @@ -94,25 +67,28 @@ def main(): | @@ -94,25 +67,28 @@ def main(): | ||
| 94 | st.error("请输入研究查询") | 67 | st.error("请输入研究查询") |
| 95 | return | 68 | return |
| 96 | 69 | ||
| 97 | - if llm_provider == "openai" and not openai_key: | ||
| 98 | - st.error("请提供OpenAI API Key") | 70 | + # 由于强制使用Gemini,检查相关的API密钥 |
| 71 | + if not GEMINI_API_KEY: | ||
| 72 | + st.error("请在您的配置文件(config.py)中设置GEMINI_API_KEY") | ||
| 73 | + return | ||
| 74 | + if not BOCHA_Web_Search_API_KEY: | ||
| 75 | + st.error("请在您的配置文件(config.py)中设置BOCHA_Web_Search_API_KEY") | ||
| 99 | return | 76 | return |
| 100 | 77 | ||
| 101 | # 自动使用配置文件中的API密钥 | 78 | # 自动使用配置文件中的API密钥 |
| 102 | - deepseek_key = DEEPSEEK_API_KEY | ||
| 103 | - gemini_key = GEMINI_API_KEY # 使用config.py中的Gemini API密钥 | 79 | + gemini_key = GEMINI_API_KEY |
| 104 | bocha_key = BOCHA_Web_Search_API_KEY | 80 | bocha_key = BOCHA_Web_Search_API_KEY |
| 105 | 81 | ||
| 106 | # 创建配置 | 82 | # 创建配置 |
| 107 | config = Config( | 83 | config = Config( |
| 108 | - deepseek_api_key=deepseek_key if llm_provider == "deepseek" else None, | ||
| 109 | - openai_api_key=openai_key if llm_provider == "openai" else None, | ||
| 110 | - gemini_api_key=gemini_key if llm_provider == "gemini" else None, | 84 | + deepseek_api_key=None, |
| 85 | + openai_api_key=None, | ||
| 86 | + gemini_api_key=gemini_key, | ||
| 111 | bocha_api_key=bocha_key, | 87 | bocha_api_key=bocha_key, |
| 112 | default_llm_provider=llm_provider, | 88 | default_llm_provider=llm_provider, |
| 113 | - deepseek_model=model_name if llm_provider == "deepseek" else "deepseek-chat", | ||
| 114 | - openai_model=model_name if llm_provider == "openai" else "gpt-4o-mini", | ||
| 115 | - gemini_model=model_name if llm_provider == "gemini" else "gemini-2.5-pro", | 89 | + deepseek_model="deepseek-chat", # 保留默认值以兼容 |
| 90 | + openai_model="gpt-4o-mini", # 保留默认值以兼容 | ||
| 91 | + gemini_model=model_name, | ||
| 116 | max_reflections=max_reflections, | 92 | max_reflections=max_reflections, |
| 117 | max_content_length=max_content_length, | 93 | max_content_length=max_content_length, |
| 118 | output_dir="media_engine_streamlit_reports" | 94 | output_dir="media_engine_streamlit_reports" |
| @@ -144,7 +120,7 @@ def execute_research(query: str, config: Config): | @@ -144,7 +120,7 @@ def execute_research(query: str, config: Config): | ||
| 144 | # 处理段落 | 120 | # 处理段落 |
| 145 | total_paragraphs = len(agent.state.paragraphs) | 121 | total_paragraphs = len(agent.state.paragraphs) |
| 146 | for i in range(total_paragraphs): | 122 | for i in range(total_paragraphs): |
| 147 | - status_text.text(f"正在处理段落 {i+1}/{total_paragraphs}: {agent.state.paragraphs[i].title}") | 123 | + status_text.text(f"正在处理段落 {i + 1}/{total_paragraphs}: {agent.state.paragraphs[i].title}") |
| 148 | 124 | ||
| 149 | # 初始搜索和总结 | 125 | # 初始搜索和总结 |
| 150 | agent._initial_search_and_summary(i) | 126 | agent._initial_search_and_summary(i) |
| @@ -181,8 +157,8 @@ def display_results(agent: DeepSearchAgent, final_report: str): | @@ -181,8 +157,8 @@ def display_results(agent: DeepSearchAgent, final_report: str): | ||
| 181 | """显示研究结果""" | 157 | """显示研究结果""" |
| 182 | st.header("研究结果") | 158 | st.header("研究结果") |
| 183 | 159 | ||
| 184 | - # 结果标签页 | ||
| 185 | - tab1, tab2, tab3 = st.tabs(["最终报告", "详细信息", "下载"]) | 160 | + # 结果标签页(已移除下载选项) |
| 161 | + tab1, tab2 = st.tabs(["最终报告", "详细信息"]) | ||
| 186 | 162 | ||
| 187 | with tab1: | 163 | with tab1: |
| 188 | st.markdown(final_report) | 164 | st.markdown(final_report) |
| @@ -191,7 +167,7 @@ def display_results(agent: DeepSearchAgent, final_report: str): | @@ -191,7 +167,7 @@ def display_results(agent: DeepSearchAgent, final_report: str): | ||
| 191 | # 段落详情 | 167 | # 段落详情 |
| 192 | st.subheader("段落详情") | 168 | st.subheader("段落详情") |
| 193 | for i, paragraph in enumerate(agent.state.paragraphs): | 169 | for i, paragraph in enumerate(agent.state.paragraphs): |
| 194 | - with st.expander(f"段落 {i+1}: {paragraph.title}"): | 170 | + with st.expander(f"段落 {i + 1}: {paragraph.title}"): |
| 195 | st.write("**预期内容:**", paragraph.content) | 171 | st.write("**预期内容:**", paragraph.content) |
| 196 | st.write("**最终内容:**", paragraph.research.latest_summary[:300] + "..." | 172 | st.write("**最终内容:**", paragraph.research.latest_summary[:300] + "..." |
| 197 | if len(paragraph.research.latest_summary) > 300 | 173 | if len(paragraph.research.latest_summary) > 300 |
| @@ -207,34 +183,14 @@ def display_results(agent: DeepSearchAgent, final_report: str): | @@ -207,34 +183,14 @@ def display_results(agent: DeepSearchAgent, final_report: str): | ||
| 207 | 183 | ||
| 208 | if all_searches: | 184 | if all_searches: |
| 209 | for i, search in enumerate(all_searches): | 185 | for i, search in enumerate(all_searches): |
| 210 | - with st.expander(f"搜索 {i+1}: {search.query}"): | 186 | + with st.expander(f"搜索 {i + 1}: {search.query}"): |
| 211 | st.write("**URL:**", search.url) | 187 | st.write("**URL:**", search.url) |
| 212 | st.write("**标题:**", search.title) | 188 | st.write("**标题:**", search.title) |
| 213 | - st.write("**内容预览:**", search.content[:200] + "..." if len(search.content) > 200 else search.content) | 189 | + st.write("**内容预览:**", |
| 190 | + search.content[:200] + "..." if len(search.content) > 200 else search.content) | ||
| 214 | if search.score: | 191 | if search.score: |
| 215 | st.write("**相关度评分:**", search.score) | 192 | st.write("**相关度评分:**", search.score) |
| 216 | 193 | ||
| 217 | - with tab3: | ||
| 218 | - # 下载选项 | ||
| 219 | - st.subheader("下载报告") | ||
| 220 | - | ||
| 221 | - # Markdown下载 | ||
| 222 | - st.download_button( | ||
| 223 | - label="下载Markdown报告", | ||
| 224 | - data=final_report, | ||
| 225 | - file_name=f"deep_search_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md", | ||
| 226 | - mime="text/markdown" | ||
| 227 | - ) | ||
| 228 | - | ||
| 229 | - # JSON状态下载 | ||
| 230 | - state_json = agent.state.to_json() | ||
| 231 | - st.download_button( | ||
| 232 | - label="下载状态文件", | ||
| 233 | - data=state_json, | ||
| 234 | - file_name=f"deep_search_state_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json", | ||
| 235 | - mime="application/json" | ||
| 236 | - ) | ||
| 237 | - | ||
| 238 | 194 | ||
| 239 | if __name__ == "__main__": | 195 | if __name__ == "__main__": |
| 240 | main() | 196 | main() |
| 1 | """ | 1 | """ |
| 2 | Streamlit Web界面 | 2 | Streamlit Web界面 |
| 3 | -为Deep Search Agent提供友好的Web界面 | 3 | +为Query Agent提供友好的Web界面 |
| 4 | """ | 4 | """ |
| 5 | 5 | ||
| 6 | import os | 6 | import os |
| @@ -19,32 +19,21 @@ from config import DEEPSEEK_API_KEY, TAVILY_API_KEY | @@ -19,32 +19,21 @@ from config import DEEPSEEK_API_KEY, TAVILY_API_KEY | ||
| 19 | def main(): | 19 | def main(): |
| 20 | """主函数""" | 20 | """主函数""" |
| 21 | st.set_page_config( | 21 | st.set_page_config( |
| 22 | - page_title="Deep Search Agent", | ||
| 23 | - page_icon="🔍", | 22 | + page_title="Query Agent", |
| 23 | + page_icon="", | ||
| 24 | layout="wide" | 24 | layout="wide" |
| 25 | ) | 25 | ) |
| 26 | 26 | ||
| 27 | - st.title("Deep Search Agent") | ||
| 28 | - st.markdown("基于DeepSeek的无框架深度搜索AI代理") | 27 | + st.title("Query Agent") |
| 28 | + st.markdown("具备强大网页搜索能力的AI代理") | ||
| 29 | 29 | ||
| 30 | - # 侧边栏配置 | ||
| 31 | - with st.sidebar: | ||
| 32 | - st.header("配置") | ||
| 33 | - | ||
| 34 | - # 高级配置 | ||
| 35 | - st.subheader("高级配置") | ||
| 36 | - max_reflections = st.slider("反思次数", 1, 5, 2) | ||
| 37 | - max_content_length = st.number_input("最大内容长度", 1000, 50000, 20000) | ||
| 38 | - | ||
| 39 | - # 模型选择 | ||
| 40 | - llm_provider = st.selectbox("LLM提供商", ["deepseek", "openai"]) | ||
| 41 | - | ||
| 42 | - if llm_provider == "deepseek": | ||
| 43 | - model_name = st.selectbox("DeepSeek模型", ["deepseek-chat"]) | ||
| 44 | - else: | ||
| 45 | - model_name = st.selectbox("OpenAI模型", ["gpt-4o-mini", "gpt-4o"]) | ||
| 46 | - openai_key = st.text_input("OpenAI API Key", type="password", | ||
| 47 | - value="") | 30 | + # ----- 配置被硬编码 ----- |
| 31 | + # 强制使用 DeepSeek | ||
| 32 | + llm_provider = "deepseek" | ||
| 33 | + model_name = "deepseek-chat" | ||
| 34 | + # 默认高级配置 | ||
| 35 | + max_reflections = 2 | ||
| 36 | + max_content_length = 20000 | ||
| 48 | 37 | ||
| 49 | # 主界面 | 38 | # 主界面 |
| 50 | col1, col2 = st.columns([2, 1]) | 39 | col1, col2 = st.columns([2, 1]) |
| @@ -57,20 +46,6 @@ def main(): | @@ -57,20 +46,6 @@ def main(): | ||
| 57 | height=100 | 46 | height=100 |
| 58 | ) | 47 | ) |
| 59 | 48 | ||
| 60 | - # 预设查询示例 | ||
| 61 | - st.subheader("示例查询") | ||
| 62 | - example_queries = [ | ||
| 63 | - "2025年人工智能发展趋势", | ||
| 64 | - "深度学习在医疗领域的应用", | ||
| 65 | - "区块链技术的最新发展", | ||
| 66 | - "可持续能源技术趋势", | ||
| 67 | - "量子计算的发展现状" | ||
| 68 | - ] | ||
| 69 | - | ||
| 70 | - selected_example = st.selectbox("选择示例查询", ["自定义"] + example_queries) | ||
| 71 | - if selected_example != "自定义": | ||
| 72 | - query = selected_example | ||
| 73 | - | ||
| 74 | with col2: | 49 | with col2: |
| 75 | st.header("状态信息") | 50 | st.header("状态信息") |
| 76 | if 'agent' in st.session_state and hasattr(st.session_state.agent, 'state'): | 51 | if 'agent' in st.session_state and hasattr(st.session_state.agent, 'state'): |
| @@ -82,8 +57,8 @@ def main(): | @@ -82,8 +57,8 @@ def main(): | ||
| 82 | st.info("尚未开始研究") | 57 | st.info("尚未开始研究") |
| 83 | 58 | ||
| 84 | # 执行按钮 | 59 | # 执行按钮 |
| 85 | - col1, col2, col3 = st.columns([1, 1, 1]) | ||
| 86 | - with col2: | 60 | + col1_btn, col2_btn, col3_btn = st.columns([1, 1, 1]) |
| 61 | + with col2_btn: | ||
| 87 | start_research = st.button("开始研究", type="primary", use_container_width=True) | 62 | start_research = st.button("开始研究", type="primary", use_container_width=True) |
| 88 | 63 | ||
| 89 | # 验证配置 | 64 | # 验证配置 |
| @@ -92,8 +67,12 @@ def main(): | @@ -92,8 +67,12 @@ def main(): | ||
| 92 | st.error("请输入研究查询") | 67 | st.error("请输入研究查询") |
| 93 | return | 68 | return |
| 94 | 69 | ||
| 95 | - if llm_provider == "openai" and not openai_key: | ||
| 96 | - st.error("请提供OpenAI API Key") | 70 | + # 由于强制使用DeepSeek,检查相关的API密钥 |
| 71 | + if not DEEPSEEK_API_KEY: | ||
| 72 | + st.error("请在您的配置文件(config.py)中设置DEEPSEEK_API_KEY") | ||
| 73 | + return | ||
| 74 | + if not TAVILY_API_KEY: | ||
| 75 | + st.error("请在您的配置文件(config.py)中设置TAVILY_API_KEY") | ||
| 97 | return | 76 | return |
| 98 | 77 | ||
| 99 | # 自动使用配置文件中的API密钥 | 78 | # 自动使用配置文件中的API密钥 |
| @@ -102,12 +81,12 @@ def main(): | @@ -102,12 +81,12 @@ def main(): | ||
| 102 | 81 | ||
| 103 | # 创建配置 | 82 | # 创建配置 |
| 104 | config = Config( | 83 | config = Config( |
| 105 | - deepseek_api_key=deepseek_key if llm_provider == "deepseek" else None, | ||
| 106 | - openai_api_key=openai_key if llm_provider == "openai" else None, | 84 | + deepseek_api_key=deepseek_key, |
| 85 | + openai_api_key=None, | ||
| 107 | tavily_api_key=tavily_key, | 86 | tavily_api_key=tavily_key, |
| 108 | default_llm_provider=llm_provider, | 87 | default_llm_provider=llm_provider, |
| 109 | - deepseek_model=model_name if llm_provider == "deepseek" else "deepseek-chat", | ||
| 110 | - openai_model=model_name if llm_provider == "openai" else "gpt-4o-mini", | 88 | + deepseek_model=model_name, |
| 89 | + openai_model="gpt-4o-mini", # 保留默认值以兼容 | ||
| 111 | max_reflections=max_reflections, | 90 | max_reflections=max_reflections, |
| 112 | max_content_length=max_content_length, | 91 | max_content_length=max_content_length, |
| 113 | output_dir="query_engine_streamlit_reports" | 92 | output_dir="query_engine_streamlit_reports" |
| @@ -139,7 +118,7 @@ def execute_research(query: str, config: Config): | @@ -139,7 +118,7 @@ def execute_research(query: str, config: Config): | ||
| 139 | # 处理段落 | 118 | # 处理段落 |
| 140 | total_paragraphs = len(agent.state.paragraphs) | 119 | total_paragraphs = len(agent.state.paragraphs) |
| 141 | for i in range(total_paragraphs): | 120 | for i in range(total_paragraphs): |
| 142 | - status_text.text(f"正在处理段落 {i+1}/{total_paragraphs}: {agent.state.paragraphs[i].title}") | 121 | + status_text.text(f"正在处理段落 {i + 1}/{total_paragraphs}: {agent.state.paragraphs[i].title}") |
| 143 | 122 | ||
| 144 | # 初始搜索和总结 | 123 | # 初始搜索和总结 |
| 145 | agent._initial_search_and_summary(i) | 124 | agent._initial_search_and_summary(i) |
| @@ -176,8 +155,8 @@ def display_results(agent: DeepSearchAgent, final_report: str): | @@ -176,8 +155,8 @@ def display_results(agent: DeepSearchAgent, final_report: str): | ||
| 176 | """显示研究结果""" | 155 | """显示研究结果""" |
| 177 | st.header("研究结果") | 156 | st.header("研究结果") |
| 178 | 157 | ||
| 179 | - # 结果标签页 | ||
| 180 | - tab1, tab2, tab3 = st.tabs(["最终报告", "详细信息", "下载"]) | 158 | + # 结果标签页(已移除下载选项) |
| 159 | + tab1, tab2 = st.tabs(["最终报告", "详细信息"]) | ||
| 181 | 160 | ||
| 182 | with tab1: | 161 | with tab1: |
| 183 | st.markdown(final_report) | 162 | st.markdown(final_report) |
| @@ -186,7 +165,7 @@ def display_results(agent: DeepSearchAgent, final_report: str): | @@ -186,7 +165,7 @@ def display_results(agent: DeepSearchAgent, final_report: str): | ||
| 186 | # 段落详情 | 165 | # 段落详情 |
| 187 | st.subheader("段落详情") | 166 | st.subheader("段落详情") |
| 188 | for i, paragraph in enumerate(agent.state.paragraphs): | 167 | for i, paragraph in enumerate(agent.state.paragraphs): |
| 189 | - with st.expander(f"段落 {i+1}: {paragraph.title}"): | 168 | + with st.expander(f"段落 {i + 1}: {paragraph.title}"): |
| 190 | st.write("**预期内容:**", paragraph.content) | 169 | st.write("**预期内容:**", paragraph.content) |
| 191 | st.write("**最终内容:**", paragraph.research.latest_summary[:300] + "..." | 170 | st.write("**最终内容:**", paragraph.research.latest_summary[:300] + "..." |
| 192 | if len(paragraph.research.latest_summary) > 300 | 171 | if len(paragraph.research.latest_summary) > 300 |
| @@ -202,34 +181,14 @@ def display_results(agent: DeepSearchAgent, final_report: str): | @@ -202,34 +181,14 @@ def display_results(agent: DeepSearchAgent, final_report: str): | ||
| 202 | 181 | ||
| 203 | if all_searches: | 182 | if all_searches: |
| 204 | for i, search in enumerate(all_searches): | 183 | for i, search in enumerate(all_searches): |
| 205 | - with st.expander(f"搜索 {i+1}: {search.query}"): | 184 | + with st.expander(f"搜索 {i + 1}: {search.query}"): |
| 206 | st.write("**URL:**", search.url) | 185 | st.write("**URL:**", search.url) |
| 207 | st.write("**标题:**", search.title) | 186 | st.write("**标题:**", search.title) |
| 208 | - st.write("**内容预览:**", search.content[:200] + "..." if len(search.content) > 200 else search.content) | 187 | + st.write("**内容预览:**", |
| 188 | + search.content[:200] + "..." if len(search.content) > 200 else search.content) | ||
| 209 | if search.score: | 189 | if search.score: |
| 210 | st.write("**相关度评分:**", search.score) | 190 | st.write("**相关度评分:**", search.score) |
| 211 | 191 | ||
| 212 | - with tab3: | ||
| 213 | - # 下载选项 | ||
| 214 | - st.subheader("下载报告") | ||
| 215 | - | ||
| 216 | - # Markdown下载 | ||
| 217 | - st.download_button( | ||
| 218 | - label="下载Markdown报告", | ||
| 219 | - data=final_report, | ||
| 220 | - file_name=f"deep_search_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md", | ||
| 221 | - mime="text/markdown" | ||
| 222 | - ) | ||
| 223 | - | ||
| 224 | - # JSON状态下载 | ||
| 225 | - state_json = agent.state.to_json() | ||
| 226 | - st.download_button( | ||
| 227 | - label="下载状态文件", | ||
| 228 | - data=state_json, | ||
| 229 | - file_name=f"deep_search_state_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json", | ||
| 230 | - mime="application/json" | ||
| 231 | - ) | ||
| 232 | - | ||
| 233 | 192 | ||
| 234 | if __name__ == "__main__": | 193 | if __name__ == "__main__": |
| 235 | main() | 194 | main() |
| 1 | +# 深度研究报告 | ||
| 2 | + | ||
| 3 | +- [深度研究报告](#深度研究报告) | ||
| 4 | + - [1. 事件全景:从5秒抓痒到18亿阅读](#1-事件全景从5秒抓痒到18亿阅读) | ||
| 5 | + - [2. 当事双方:谁在风暴中心](#2-当事双方谁在风暴中心) | ||
| 6 | + - [3. 舆论温度表:数据·声浪·情绪](#3-舆论温度表数据声浪情绪) | ||
| 7 | + - [3.1 平台热度排行榜](#31-平台热度排行榜) | ||
| 8 | + - [3.2 学生集体表情](#32-学生集体表情) | ||
| 9 | + - [4. 现行防治机制:漏洞与鸿沟](#4-现行防治机制漏洞与鸿沟) | ||
| 10 | + - [5. 改革呼声:制度补洞,人心如何补](#5-改革呼声制度补洞人心如何补) | ||
| 11 | + - [5.1 针锋相对的两种方案](#51-针锋相对的两种方案) | ||
| 12 | + - [5.2 学生真实焦虑](#52-学生真实焦虑) | ||
| 13 | + - [6. 结论:当樱花再次飘落](#6-结论当樱花再次飘落) | ||
| 14 | + | ||
| 15 | +--- | ||
| 16 | + | ||
| 17 | +## 1. 事件全景:从5秒抓痒到18亿阅读 | ||
| 18 | +| 时间节点 | 关键动作 | 全网热度 | | ||
| 19 | +|---|---|---| | ||
| 20 | +| **2023-07-11** | 肖同学抓痒被拍;杨某媛现场要求手写“道歉” | 校内口口相传 | | ||
| 21 | +| **2023-10-11** | 杨某媛凌晨发布剪辑视频,贴“性骚扰”标签 | 4小时破亿阅读 | | ||
| 22 | +| **2023-10-13** | 学校红头文件:记过处分,取消保研资格 | 微博热搜第一 | | ||
| 23 | +| **2025-07-25** | 法院一审:*“无法认定性骚扰”* | #武大仍未撤销处分# 3.7亿阅读 | | ||
| 24 | +| **2025-07-27** | 杨某媛晒香港浸会大学博士录取 | 小红书3.2万赞“姐姐好飒” | | ||
| 25 | +| **2025-07-31** | 校长回应“等上级安排” | B站弹幕刷屏“青春谁来赔” | | ||
| 26 | + | ||
| 27 | +--- | ||
| 28 | + | ||
| 29 | +## 2. 当事双方:谁在风暴中心 | ||
| 30 | + | ||
| 31 | +| 维度 | 肖同学(19岁,本科) | 杨某媛(22岁,研二) | | ||
| 32 | +|---|---|---| | ||
| 33 | +| **现实代价** | - PTSD确诊<br>- 爷爷去世<br>- 保研名额归零 | - 获名校录取<br>- 被贴“诬告者”标签<br>- 论文漏洞遭群嘲 | | ||
| 34 | +| **网络处境** | 私信辱骂、家庭住址被曝光 | 小红书“飒姐”人设与“学术妲己”并存 | | ||
| 35 | +| **制度结果** | 记过处分仍挂官网 | 无校纪追责 | | ||
| 36 | + | ||
| 37 | +--- | ||
| 38 | + | ||
| 39 | +## 3. 舆论温度表:数据·声浪·情绪 | ||
| 40 | + | ||
| 41 | +### 3.1 平台热度排行榜 | ||
| 42 | +| 平台 | 主话题阅读量/播放量 | 最高同时在线 | | ||
| 43 | +|---|---|---| | ||
| 44 | +| 微博 | 18.4亿 | 62%情绪为“愤怒” | | ||
| 45 | +| 抖音 | 12.7亿 | “气死了”弹幕 3.7条/10秒 | | ||
| 46 | +| 知乎 | 4.2万条回答 | 热帖“为什么高校举报石沉大海” | | ||
| 47 | +| B站 | 50万+弹幕 | “樱花没开,我们也没脸开”刷屏 | | ||
| 48 | +| 小红书 | 900万+ #我也遇到过# | “恐惧”指数↑12个百分点 | | ||
| 49 | + | ||
| 50 | +### 3.2 学生集体表情 | ||
| 51 | +- **珞珈山水BBS**:深夜在线4100+,热帖《旧图书馆的猫》2.3万点亮 | ||
| 52 | +- **匿名树洞**:“我们不是沉默,是怕成为下一个杨某” 1.1万赞 | ||
| 53 | +- **微信群/QQ群**统一刷屏: | ||
| 54 | + > “如果樱花会说话,它会哭吗?” | ||
| 55 | + | ||
| 56 | +--- | ||
| 57 | + | ||
| 58 | +## 4. 现行防治机制:漏洞与鸿沟 | ||
| 59 | + | ||
| 60 | +| 制度环节 | 学生遭遇 | 舆情高频词 | | ||
| 61 | +|---|---|---| | ||
| 62 | +| **举报入口** | 按钮形同虚设,需“两名证人签字” | “证据链陷阱” | | ||
| 63 | +| **调查流程** | 3个月无书面回复,信息被群发泄露 | “裸奔式举报” | | ||
| 64 | +| **心理支持** | 心理评估报告被质疑“主观” | “二次伤害” | | ||
| 65 | +| **结果反馈** | 多数仅为“谈话提醒” | “息事宁人” | | ||
| 66 | + | ||
| 67 | +> “我们怕的不是色狼,而是色狼背后那张‘维护学校声誉’的遮羞布。” | ||
| 68 | +> ——微博高赞留言,转发5.2万次 | ||
| 69 | + | ||
| 70 | +--- | ||
| 71 | + | ||
| 72 | +## 5. 改革呼声:制度补洞,人心如何补 | ||
| 73 | + | ||
| 74 | +### 5.1 针锋相对的两种方案 | ||
| 75 | +| 主张方 | 核心观点 | 代表语录 | | ||
| 76 | +|---|---|---| | ||
| 77 | +| **北大法学院教授** | 法院未认定即自动冻结校纪处分 | “行政权不能凌驾司法权” | | ||
| 78 | +| **华东师大性别研究基地** | 建立“司法—校纪”双轨听证 | “让双方都能说话” | | ||
| 79 | + | ||
| 80 | +### 5.2 学生真实焦虑 | ||
| 81 | +- **问卷数据**:45%选择“说不清”现有措施能否让自己更安心 | ||
| 82 | +- **深夜留言**: | ||
| 83 | + > “我更怕风吹草动时,第一反应是‘我会不会被二次伤害’。” | ||
| 84 | + | ||
| 85 | +--- | ||
| 86 | + | ||
| 87 | +## 6. 结论:当樱花再次飘落 | ||
| 88 | + | ||
| 89 | +1. **真相跑不赢情绪**:从5秒抓痒到18亿阅读,网络审判只用了4小时。 | ||
| 90 | +2. **制度性缺位**:封闭调查、信息泄露、权力不对等,让学生不敢按下“发送”。 | ||
| 91 | +3. **双输结局**:肖同学失去前途与健康,杨某媛背负标签与质疑,学校公信力折损。 | ||
| 92 | +4. **改革关键**: | ||
| 93 | + - 司法结果与校纪处分**刚性挂钩** | ||
| 94 | + - 建立**第三方独立调查+隐私保护**双保险 | ||
| 95 | + - 把“零”从摄像头数量转向**每个人心里的那杆秤** | ||
| 96 | + | ||
| 97 | +> 樱花会再次盛开,但落在地上的花瓣提醒我们: | ||
| 98 | +> **如果制度不补洞,明年的风还会吹来新的眼泪。** |
This diff could not be displayed because it is too large.
| 1 | +# 深度研究报告 | ||
| 2 | + | ||
| 3 | +- [深度研究报告](#深度研究报告) | ||
| 4 | + - [1. 事件全景:从5秒抓痒到18亿阅读](#1-事件全景从5秒抓痒到18亿阅读) | ||
| 5 | + - [2. 当事双方:谁在风暴中心](#2-当事双方谁在风暴中心) | ||
| 6 | + - [3. 舆论温度表:数据·声浪·情绪](#3-舆论温度表数据声浪情绪) | ||
| 7 | + - [3.1 平台热度排行榜](#31-平台热度排行榜) | ||
| 8 | + - [3.2 学生集体表情](#32-学生集体表情) | ||
| 9 | + - [4. 现行防治机制:漏洞与鸿沟](#4-现行防治机制漏洞与鸿沟) | ||
| 10 | + - [5. 改革呼声:制度补洞,人心如何补](#5-改革呼声制度补洞人心如何补) | ||
| 11 | + - [5.1 针锋相对的两种方案](#51-针锋相对的两种方案) | ||
| 12 | + - [5.2 学生真实焦虑](#52-学生真实焦虑) | ||
| 13 | + - [6. 结论:当樱花再次飘落](#6-结论当樱花再次飘落) | ||
| 14 | + | ||
| 15 | +--- | ||
| 16 | + | ||
| 17 | +## 1. 事件全景:从5秒抓痒到18亿阅读 | ||
| 18 | +| 时间节点 | 关键动作 | 全网热度 | | ||
| 19 | +|---|---|---| | ||
| 20 | +| **2023-07-11** | 肖同学抓痒被拍;杨某媛现场要求手写“道歉” | 校内口口相传 | | ||
| 21 | +| **2023-10-11** | 杨某媛凌晨发布剪辑视频,贴“性骚扰”标签 | 4小时破亿阅读 | | ||
| 22 | +| **2023-10-13** | 学校红头文件:记过处分,取消保研资格 | 微博热搜第一 | | ||
| 23 | +| **2025-07-25** | 法院一审:*“无法认定性骚扰”* | #武大仍未撤销处分# 3.7亿阅读 | | ||
| 24 | +| **2025-07-27** | 杨某媛晒香港浸会大学博士录取 | 小红书3.2万赞“姐姐好飒” | | ||
| 25 | +| **2025-07-31** | 校长回应“等上级安排” | B站弹幕刷屏“青春谁来赔” | | ||
| 26 | + | ||
| 27 | +--- | ||
| 28 | + | ||
| 29 | +## 2. 当事双方:谁在风暴中心 | ||
| 30 | + | ||
| 31 | +| 维度 | 肖同学(19岁,本科) | 杨某媛(22岁,研二) | | ||
| 32 | +|---|---|---| | ||
| 33 | +| **现实代价** | - PTSD确诊<br>- 爷爷去世<br>- 保研名额归零 | - 获名校录取<br>- 被贴“诬告者”标签<br>- 论文漏洞遭群嘲 | | ||
| 34 | +| **网络处境** | 私信辱骂、家庭住址被曝光 | 小红书“飒姐”人设与“学术妲己”并存 | | ||
| 35 | +| **制度结果** | 记过处分仍挂官网 | 无校纪追责 | | ||
| 36 | + | ||
| 37 | +--- | ||
| 38 | + | ||
| 39 | +## 3. 舆论温度表:数据·声浪·情绪 | ||
| 40 | + | ||
| 41 | +### 3.1 平台热度排行榜 | ||
| 42 | +| 平台 | 主话题阅读量/播放量 | 最高同时在线 | | ||
| 43 | +|---|---|---| | ||
| 44 | +| 微博 | 18.4亿 | 62%情绪为“愤怒” | | ||
| 45 | +| 抖音 | 12.7亿 | “气死了”弹幕 3.7条/10秒 | | ||
| 46 | +| 知乎 | 4.2万条回答 | 热帖“为什么高校举报石沉大海” | | ||
| 47 | +| B站 | 50万+弹幕 | “樱花没开,我们也没脸开”刷屏 | | ||
| 48 | +| 小红书 | 900万+ #我也遇到过# | “恐惧”指数↑12个百分点 | | ||
| 49 | + | ||
| 50 | +### 3.2 学生集体表情 | ||
| 51 | +- **珞珈山水BBS**:深夜在线4100+,热帖《旧图书馆的猫》2.3万点亮 | ||
| 52 | +- **匿名树洞**:“我们不是沉默,是怕成为下一个杨某” 1.1万赞 | ||
| 53 | +- **微信群/QQ群**统一刷屏: | ||
| 54 | + > “如果樱花会说话,它会哭吗?” | ||
| 55 | + | ||
| 56 | +--- | ||
| 57 | + | ||
| 58 | +## 4. 现行防治机制:漏洞与鸿沟 | ||
| 59 | + | ||
| 60 | +| 制度环节 | 学生遭遇 | 舆情高频词 | | ||
| 61 | +|---|---|---| | ||
| 62 | +| **举报入口** | 按钮形同虚设,需“两名证人签字” | “证据链陷阱” | | ||
| 63 | +| **调查流程** | 3个月无书面回复,信息被群发泄露 | “裸奔式举报” | | ||
| 64 | +| **心理支持** | 心理评估报告被质疑“主观” | “二次伤害” | | ||
| 65 | +| **结果反馈** | 多数仅为“谈话提醒” | “息事宁人” | | ||
| 66 | + | ||
| 67 | +> “我们怕的不是色狼,而是色狼背后那张‘维护学校声誉’的遮羞布。” | ||
| 68 | +> ——微博高赞留言,转发5.2万次 | ||
| 69 | + | ||
| 70 | +--- | ||
| 71 | + | ||
| 72 | +## 5. 改革呼声:制度补洞,人心如何补 | ||
| 73 | + | ||
| 74 | +### 5.1 针锋相对的两种方案 | ||
| 75 | +| 主张方 | 核心观点 | 代表语录 | | ||
| 76 | +|---|---|---| | ||
| 77 | +| **北大法学院教授** | 法院未认定即自动冻结校纪处分 | “行政权不能凌驾司法权” | | ||
| 78 | +| **华东师大性别研究基地** | 建立“司法—校纪”双轨听证 | “让双方都能说话” | | ||
| 79 | + | ||
| 80 | +### 5.2 学生真实焦虑 | ||
| 81 | +- **问卷数据**:45%选择“说不清”现有措施能否让自己更安心 | ||
| 82 | +- **深夜留言**: | ||
| 83 | + > “我更怕风吹草动时,第一反应是‘我会不会被二次伤害’。” | ||
| 84 | + | ||
| 85 | +--- | ||
| 86 | + | ||
| 87 | +## 6. 结论:当樱花再次飘落 | ||
| 88 | + | ||
| 89 | +1. **真相跑不赢情绪**:从5秒抓痒到18亿阅读,网络审判只用了4小时。 | ||
| 90 | +2. **制度性缺位**:封闭调查、信息泄露、权力不对等,让学生不敢按下“发送”。 | ||
| 91 | +3. **双输结局**:肖同学失去前途与健康,杨某媛背负标签与质疑,学校公信力折损。 | ||
| 92 | +4. **改革关键**: | ||
| 93 | + - 司法结果与校纪处分**刚性挂钩** | ||
| 94 | + - 建立**第三方独立调查+隐私保护**双保险 | ||
| 95 | + - 把“零”从摄像头数量转向**每个人心里的那杆秤** | ||
| 96 | + | ||
| 97 | +> 樱花会再次盛开,但落在地上的花瓣提醒我们: | ||
| 98 | +> **如果制度不补洞,明年的风还会吹来新的眼泪。** |
| 1 | +""" | ||
| 2 | +Streamlit Web界面 | ||
| 3 | +为DInsight Agent提供友好的Web界面 | ||
| 4 | +""" | ||
| 5 | + | ||
| 6 | +import os | ||
| 7 | +import sys | ||
| 8 | +import streamlit as st | ||
| 9 | +from datetime import datetime | ||
| 10 | +import json | ||
| 11 | + | ||
| 12 | +# 添加src目录到Python路径 | ||
| 13 | +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) | ||
| 14 | + | ||
| 15 | +from InsightEngine import DeepSearchAgent, Config | ||
| 16 | +from config import DEEPSEEK_API_KEY, KIMI_API_KEY, DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT, DB_CHARSET | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +def main(): | ||
| 20 | + """主函数""" | ||
| 21 | + st.set_page_config( | ||
| 22 | + page_title="Insight Agent", | ||
| 23 | + page_icon="", | ||
| 24 | + layout="wide" | ||
| 25 | + ) | ||
| 26 | + | ||
| 27 | + st.title("Insight Engine") | ||
| 28 | + st.markdown("本地舆情数据库深度分析AI代理") | ||
| 29 | + | ||
| 30 | + # ----- 配置被硬编码 ----- | ||
| 31 | + # 强制使用 Kimi | ||
| 32 | + llm_provider = "kimi" | ||
| 33 | + model_name = "kimi-k2-0711-preview" | ||
| 34 | + # 默认高级配置 | ||
| 35 | + max_reflections = 2 | ||
| 36 | + max_content_length = 500000 # Kimi支持长文本 | ||
| 37 | + | ||
| 38 | + # 主界面 | ||
| 39 | + col1, col2 = st.columns([2, 1]) | ||
| 40 | + | ||
| 41 | + with col1: | ||
| 42 | + st.header("研究查询") | ||
| 43 | + query = st.text_area( | ||
| 44 | + "请输入您要研究的问题", | ||
| 45 | + placeholder="例如:2025年人工智能发展趋势", | ||
| 46 | + height=100 | ||
| 47 | + ) | ||
| 48 | + | ||
| 49 | + with col2: | ||
| 50 | + st.header("状态信息") | ||
| 51 | + if 'agent' in st.session_state and hasattr(st.session_state.agent, 'state'): | ||
| 52 | + progress = st.session_state.agent.get_progress_summary() | ||
| 53 | + st.metric("总段落数", progress['total_paragraphs']) | ||
| 54 | + st.metric("已完成", progress['completed_paragraphs']) | ||
| 55 | + st.progress(progress['progress_percentage'] / 100) | ||
| 56 | + else: | ||
| 57 | + st.info("尚未开始研究") | ||
| 58 | + | ||
| 59 | + # 执行按钮 | ||
| 60 | + col1_btn, col2_btn, col3_btn = st.columns([1, 1, 1]) | ||
| 61 | + with col2_btn: | ||
| 62 | + start_research = st.button("开始研究", type="primary", use_container_width=True) | ||
| 63 | + | ||
| 64 | + # 验证配置 | ||
| 65 | + if start_research: | ||
| 66 | + if not query.strip(): | ||
| 67 | + st.error("请输入研究查询") | ||
| 68 | + return | ||
| 69 | + | ||
| 70 | + # 由于强制使用Kimi,只检查KIMI_API_KEY | ||
| 71 | + if not KIMI_API_KEY: | ||
| 72 | + st.error("请在您的配置文件(config.py)中设置KIMI_API_KEY") | ||
| 73 | + return | ||
| 74 | + | ||
| 75 | + # 自动使用配置文件中的API密钥和数据库配置 | ||
| 76 | + db_host = DB_HOST | ||
| 77 | + db_user = DB_USER | ||
| 78 | + db_password = DB_PASSWORD | ||
| 79 | + db_name = DB_NAME | ||
| 80 | + db_port = DB_PORT | ||
| 81 | + db_charset = DB_CHARSET | ||
| 82 | + | ||
| 83 | + # 创建配置 | ||
| 84 | + config = Config( | ||
| 85 | + deepseek_api_key=None, | ||
| 86 | + openai_api_key=None, | ||
| 87 | + kimi_api_key=KIMI_API_KEY, # 强制使用配置文件中的Kimi Key | ||
| 88 | + db_host=db_host, | ||
| 89 | + db_user=db_user, | ||
| 90 | + db_password=db_password, | ||
| 91 | + db_name=db_name, | ||
| 92 | + db_port=db_port, | ||
| 93 | + db_charset=db_charset, | ||
| 94 | + default_llm_provider=llm_provider, | ||
| 95 | + deepseek_model="deepseek-chat", # 保留默认值以兼容 | ||
| 96 | + openai_model="gpt-4o-mini", # 保留默认值以兼容 | ||
| 97 | + kimi_model=model_name, | ||
| 98 | + max_reflections=max_reflections, | ||
| 99 | + max_content_length=max_content_length, | ||
| 100 | + output_dir="insight_engine_streamlit_reports" | ||
| 101 | + ) | ||
| 102 | + | ||
| 103 | + # 执行研究 | ||
| 104 | + execute_research(query, config) | ||
| 105 | + | ||
| 106 | + | ||
| 107 | +def execute_research(query: str, config: Config): | ||
| 108 | + """执行研究""" | ||
| 109 | + try: | ||
| 110 | + # 创建进度条 | ||
| 111 | + progress_bar = st.progress(0) | ||
| 112 | + status_text = st.empty() | ||
| 113 | + | ||
| 114 | + # 初始化Agent | ||
| 115 | + status_text.text("正在初始化Agent...") | ||
| 116 | + agent = DeepSearchAgent(config) | ||
| 117 | + st.session_state.agent = agent | ||
| 118 | + | ||
| 119 | + progress_bar.progress(10) | ||
| 120 | + | ||
| 121 | + # 生成报告结构 | ||
| 122 | + status_text.text("正在生成报告结构...") | ||
| 123 | + agent._generate_report_structure(query) | ||
| 124 | + progress_bar.progress(20) | ||
| 125 | + | ||
| 126 | + # 处理段落 | ||
| 127 | + total_paragraphs = len(agent.state.paragraphs) | ||
| 128 | + for i in range(total_paragraphs): | ||
| 129 | + status_text.text(f"正在处理段落 {i + 1}/{total_paragraphs}: {agent.state.paragraphs[i].title}") | ||
| 130 | + | ||
| 131 | + # 初始搜索和总结 | ||
| 132 | + agent._initial_search_and_summary(i) | ||
| 133 | + progress_value = 20 + (i + 0.5) / total_paragraphs * 60 | ||
| 134 | + progress_bar.progress(int(progress_value)) | ||
| 135 | + | ||
| 136 | + # 反思循环 | ||
| 137 | + agent._reflection_loop(i) | ||
| 138 | + agent.state.paragraphs[i].research.mark_completed() | ||
| 139 | + | ||
| 140 | + progress_value = 20 + (i + 1) / total_paragraphs * 60 | ||
| 141 | + progress_bar.progress(int(progress_value)) | ||
| 142 | + | ||
| 143 | + # 生成最终报告 | ||
| 144 | + status_text.text("正在生成最终报告...") | ||
| 145 | + final_report = agent._generate_final_report() | ||
| 146 | + progress_bar.progress(90) | ||
| 147 | + | ||
| 148 | + # 保存报告 | ||
| 149 | + status_text.text("正在保存报告...") | ||
| 150 | + agent._save_report(final_report) | ||
| 151 | + progress_bar.progress(100) | ||
| 152 | + | ||
| 153 | + status_text.text("研究完成!") | ||
| 154 | + | ||
| 155 | + # 显示结果 | ||
| 156 | + display_results(agent, final_report) | ||
| 157 | + | ||
| 158 | + except Exception as e: | ||
| 159 | + st.error(f"研究过程中发生错误: {str(e)}") | ||
| 160 | + | ||
| 161 | + | ||
| 162 | +def display_results(agent: DeepSearchAgent, final_report: str): | ||
| 163 | + """显示研究结果""" | ||
| 164 | + st.header("研究结果") | ||
| 165 | + | ||
| 166 | + # 结果标签页(已移除下载选项) | ||
| 167 | + tab1, tab2 = st.tabs(["最终报告", "详细信息"]) | ||
| 168 | + | ||
| 169 | + with tab1: | ||
| 170 | + st.markdown(final_report) | ||
| 171 | + | ||
| 172 | + with tab2: | ||
| 173 | + # 段落详情 | ||
| 174 | + st.subheader("段落详情") | ||
| 175 | + for i, paragraph in enumerate(agent.state.paragraphs): | ||
| 176 | + with st.expander(f"段落 {i + 1}: {paragraph.title}"): | ||
| 177 | + st.write("**预期内容:**", paragraph.content) | ||
| 178 | + st.write("**最终内容:**", paragraph.research.latest_summary[:300] + "..." | ||
| 179 | + if len(paragraph.research.latest_summary) > 300 | ||
| 180 | + else paragraph.research.latest_summary) | ||
| 181 | + st.write("**搜索次数:**", paragraph.research.get_search_count()) | ||
| 182 | + st.write("**反思次数:**", paragraph.research.reflection_iteration) | ||
| 183 | + | ||
| 184 | + # 搜索历史 | ||
| 185 | + st.subheader("搜索历史") | ||
| 186 | + all_searches = [] | ||
| 187 | + for paragraph in agent.state.paragraphs: | ||
| 188 | + all_searches.extend(paragraph.research.search_history) | ||
| 189 | + | ||
| 190 | + if all_searches: | ||
| 191 | + for i, search in enumerate(all_searches): | ||
| 192 | + with st.expander(f"搜索 {i + 1}: {search.query}"): | ||
| 193 | + st.write("**URL:**", search.url) | ||
| 194 | + st.write("**标题:**", search.title) | ||
| 195 | + st.write("**内容预览:**", | ||
| 196 | + search.content[:200] + "..." if len(search.content) > 200 else search.content) | ||
| 197 | + if search.score: | ||
| 198 | + st.write("**相关度评分:**", search.score) | ||
| 199 | + | ||
| 200 | + | ||
| 201 | +if __name__ == "__main__": | ||
| 202 | + main() |
This diff could not be displayed because it is too large.
| 1 | +# 深度研究报告 | ||
| 2 | + | ||
| 3 | +- [深度研究报告](#深度研究报告) | ||
| 4 | + - [1. 事件全景:从5秒抓痒到18亿阅读](#1-事件全景从5秒抓痒到18亿阅读) | ||
| 5 | + - [2. 当事双方:谁在风暴中心](#2-当事双方谁在风暴中心) | ||
| 6 | + - [3. 舆论温度表:数据·声浪·情绪](#3-舆论温度表数据声浪情绪) | ||
| 7 | + - [3.1 平台热度排行榜](#31-平台热度排行榜) | ||
| 8 | + - [3.2 学生集体表情](#32-学生集体表情) | ||
| 9 | + - [4. 现行防治机制:漏洞与鸿沟](#4-现行防治机制漏洞与鸿沟) | ||
| 10 | + - [5. 改革呼声:制度补洞,人心如何补](#5-改革呼声制度补洞人心如何补) | ||
| 11 | + - [5.1 针锋相对的两种方案](#51-针锋相对的两种方案) | ||
| 12 | + - [5.2 学生真实焦虑](#52-学生真实焦虑) | ||
| 13 | + - [6. 结论:当樱花再次飘落](#6-结论当樱花再次飘落) | ||
| 14 | + | ||
| 15 | +--- | ||
| 16 | + | ||
| 17 | +## 1. 事件全景:从5秒抓痒到18亿阅读 | ||
| 18 | +| 时间节点 | 关键动作 | 全网热度 | | ||
| 19 | +|---|---|---| | ||
| 20 | +| **2023-07-11** | 肖同学抓痒被拍;杨某媛现场要求手写“道歉” | 校内口口相传 | | ||
| 21 | +| **2023-10-11** | 杨某媛凌晨发布剪辑视频,贴“性骚扰”标签 | 4小时破亿阅读 | | ||
| 22 | +| **2023-10-13** | 学校红头文件:记过处分,取消保研资格 | 微博热搜第一 | | ||
| 23 | +| **2025-07-25** | 法院一审:*“无法认定性骚扰”* | #武大仍未撤销处分# 3.7亿阅读 | | ||
| 24 | +| **2025-07-27** | 杨某媛晒香港浸会大学博士录取 | 小红书3.2万赞“姐姐好飒” | | ||
| 25 | +| **2025-07-31** | 校长回应“等上级安排” | B站弹幕刷屏“青春谁来赔” | | ||
| 26 | + | ||
| 27 | +--- | ||
| 28 | + | ||
| 29 | +## 2. 当事双方:谁在风暴中心 | ||
| 30 | + | ||
| 31 | +| 维度 | 肖同学(19岁,本科) | 杨某媛(22岁,研二) | | ||
| 32 | +|---|---|---| | ||
| 33 | +| **现实代价** | - PTSD确诊<br>- 爷爷去世<br>- 保研名额归零 | - 获名校录取<br>- 被贴“诬告者”标签<br>- 论文漏洞遭群嘲 | | ||
| 34 | +| **网络处境** | 私信辱骂、家庭住址被曝光 | 小红书“飒姐”人设与“学术妲己”并存 | | ||
| 35 | +| **制度结果** | 记过处分仍挂官网 | 无校纪追责 | | ||
| 36 | + | ||
| 37 | +--- | ||
| 38 | + | ||
| 39 | +## 3. 舆论温度表:数据·声浪·情绪 | ||
| 40 | + | ||
| 41 | +### 3.1 平台热度排行榜 | ||
| 42 | +| 平台 | 主话题阅读量/播放量 | 最高同时在线 | | ||
| 43 | +|---|---|---| | ||
| 44 | +| 微博 | 18.4亿 | 62%情绪为“愤怒” | | ||
| 45 | +| 抖音 | 12.7亿 | “气死了”弹幕 3.7条/10秒 | | ||
| 46 | +| 知乎 | 4.2万条回答 | 热帖“为什么高校举报石沉大海” | | ||
| 47 | +| B站 | 50万+弹幕 | “樱花没开,我们也没脸开”刷屏 | | ||
| 48 | +| 小红书 | 900万+ #我也遇到过# | “恐惧”指数↑12个百分点 | | ||
| 49 | + | ||
| 50 | +### 3.2 学生集体表情 | ||
| 51 | +- **珞珈山水BBS**:深夜在线4100+,热帖《旧图书馆的猫》2.3万点亮 | ||
| 52 | +- **匿名树洞**:“我们不是沉默,是怕成为下一个杨某” 1.1万赞 | ||
| 53 | +- **微信群/QQ群**统一刷屏: | ||
| 54 | + > “如果樱花会说话,它会哭吗?” | ||
| 55 | + | ||
| 56 | +--- | ||
| 57 | + | ||
| 58 | +## 4. 现行防治机制:漏洞与鸿沟 | ||
| 59 | + | ||
| 60 | +| 制度环节 | 学生遭遇 | 舆情高频词 | | ||
| 61 | +|---|---|---| | ||
| 62 | +| **举报入口** | 按钮形同虚设,需“两名证人签字” | “证据链陷阱” | | ||
| 63 | +| **调查流程** | 3个月无书面回复,信息被群发泄露 | “裸奔式举报” | | ||
| 64 | +| **心理支持** | 心理评估报告被质疑“主观” | “二次伤害” | | ||
| 65 | +| **结果反馈** | 多数仅为“谈话提醒” | “息事宁人” | | ||
| 66 | + | ||
| 67 | +> “我们怕的不是色狼,而是色狼背后那张‘维护学校声誉’的遮羞布。” | ||
| 68 | +> ——微博高赞留言,转发5.2万次 | ||
| 69 | + | ||
| 70 | +--- | ||
| 71 | + | ||
| 72 | +## 5. 改革呼声:制度补洞,人心如何补 | ||
| 73 | + | ||
| 74 | +### 5.1 针锋相对的两种方案 | ||
| 75 | +| 主张方 | 核心观点 | 代表语录 | | ||
| 76 | +|---|---|---| | ||
| 77 | +| **北大法学院教授** | 法院未认定即自动冻结校纪处分 | “行政权不能凌驾司法权” | | ||
| 78 | +| **华东师大性别研究基地** | 建立“司法—校纪”双轨听证 | “让双方都能说话” | | ||
| 79 | + | ||
| 80 | +### 5.2 学生真实焦虑 | ||
| 81 | +- **问卷数据**:45%选择“说不清”现有措施能否让自己更安心 | ||
| 82 | +- **深夜留言**: | ||
| 83 | + > “我更怕风吹草动时,第一反应是‘我会不会被二次伤害’。” | ||
| 84 | + | ||
| 85 | +--- | ||
| 86 | + | ||
| 87 | +## 6. 结论:当樱花再次飘落 | ||
| 88 | + | ||
| 89 | +1. **真相跑不赢情绪**:从5秒抓痒到18亿阅读,网络审判只用了4小时。 | ||
| 90 | +2. **制度性缺位**:封闭调查、信息泄露、权力不对等,让学生不敢按下“发送”。 | ||
| 91 | +3. **双输结局**:肖同学失去前途与健康,杨某媛背负标签与质疑,学校公信力折损。 | ||
| 92 | +4. **改革关键**: | ||
| 93 | + - 司法结果与校纪处分**刚性挂钩** | ||
| 94 | + - 建立**第三方独立调查+隐私保护**双保险 | ||
| 95 | + - 把“零”从摄像头数量转向**每个人心里的那杆秤** | ||
| 96 | + | ||
| 97 | +> 樱花会再次盛开,但落在地上的花瓣提醒我们: | ||
| 98 | +> **如果制度不补洞,明年的风还会吹来新的眼泪。** |
| 1 | +""" | ||
| 2 | +Streamlit Web界面 | ||
| 3 | +为DInsight Agent提供友好的Web界面 | ||
| 4 | +""" | ||
| 5 | + | ||
| 6 | +import os | ||
| 7 | +import sys | ||
| 8 | +import streamlit as st | ||
| 9 | +from datetime import datetime | ||
| 10 | +import json | ||
| 11 | + | ||
| 12 | +# 添加src目录到Python路径 | ||
| 13 | +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) | ||
| 14 | + | ||
| 15 | +from InsightEngine import DeepSearchAgent, Config | ||
| 16 | +from config import DEEPSEEK_API_KEY, KIMI_API_KEY, DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT, DB_CHARSET | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +def main(): | ||
| 20 | + """主函数""" | ||
| 21 | + st.set_page_config( | ||
| 22 | + page_title="Insight Agent", | ||
| 23 | + page_icon="", | ||
| 24 | + layout="wide" | ||
| 25 | + ) | ||
| 26 | + | ||
| 27 | + st.title("Insight Engine") | ||
| 28 | + st.markdown("本地舆情数据库深度分析AI代理") | ||
| 29 | + | ||
| 30 | + # ----- 配置被硬编码 ----- | ||
| 31 | + # 强制使用 Kimi | ||
| 32 | + llm_provider = "kimi" | ||
| 33 | + model_name = "kimi-k2-0711-preview" | ||
| 34 | + # 默认高级配置 | ||
| 35 | + max_reflections = 2 | ||
| 36 | + max_content_length = 500000 # Kimi支持长文本 | ||
| 37 | + | ||
| 38 | + # 主界面 | ||
| 39 | + col1, col2 = st.columns([2, 1]) | ||
| 40 | + | ||
| 41 | + with col1: | ||
| 42 | + st.header("研究查询") | ||
| 43 | + query = st.text_area( | ||
| 44 | + "请输入您要研究的问题", | ||
| 45 | + placeholder="例如:2025年人工智能发展趋势", | ||
| 46 | + height=100 | ||
| 47 | + ) | ||
| 48 | + | ||
| 49 | + with col2: | ||
| 50 | + st.header("状态信息") | ||
| 51 | + if 'agent' in st.session_state and hasattr(st.session_state.agent, 'state'): | ||
| 52 | + progress = st.session_state.agent.get_progress_summary() | ||
| 53 | + st.metric("总段落数", progress['total_paragraphs']) | ||
| 54 | + st.metric("已完成", progress['completed_paragraphs']) | ||
| 55 | + st.progress(progress['progress_percentage'] / 100) | ||
| 56 | + else: | ||
| 57 | + st.info("尚未开始研究") | ||
| 58 | + | ||
| 59 | + # 执行按钮 | ||
| 60 | + col1_btn, col2_btn, col3_btn = st.columns([1, 1, 1]) | ||
| 61 | + with col2_btn: | ||
| 62 | + start_research = st.button("开始研究", type="primary", use_container_width=True) | ||
| 63 | + | ||
| 64 | + # 验证配置 | ||
| 65 | + if start_research: | ||
| 66 | + if not query.strip(): | ||
| 67 | + st.error("请输入研究查询") | ||
| 68 | + return | ||
| 69 | + | ||
| 70 | + # 由于强制使用Kimi,只检查KIMI_API_KEY | ||
| 71 | + if not KIMI_API_KEY: | ||
| 72 | + st.error("请在您的配置文件(config.py)中设置KIMI_API_KEY") | ||
| 73 | + return | ||
| 74 | + | ||
| 75 | + # 自动使用配置文件中的API密钥和数据库配置 | ||
| 76 | + db_host = DB_HOST | ||
| 77 | + db_user = DB_USER | ||
| 78 | + db_password = DB_PASSWORD | ||
| 79 | + db_name = DB_NAME | ||
| 80 | + db_port = DB_PORT | ||
| 81 | + db_charset = DB_CHARSET | ||
| 82 | + | ||
| 83 | + # 创建配置 | ||
| 84 | + config = Config( | ||
| 85 | + deepseek_api_key=None, | ||
| 86 | + openai_api_key=None, | ||
| 87 | + kimi_api_key=KIMI_API_KEY, # 强制使用配置文件中的Kimi Key | ||
| 88 | + db_host=db_host, | ||
| 89 | + db_user=db_user, | ||
| 90 | + db_password=db_password, | ||
| 91 | + db_name=db_name, | ||
| 92 | + db_port=db_port, | ||
| 93 | + db_charset=db_charset, | ||
| 94 | + default_llm_provider=llm_provider, | ||
| 95 | + deepseek_model="deepseek-chat", # 保留默认值以兼容 | ||
| 96 | + openai_model="gpt-4o-mini", # 保留默认值以兼容 | ||
| 97 | + kimi_model=model_name, | ||
| 98 | + max_reflections=max_reflections, | ||
| 99 | + max_content_length=max_content_length, | ||
| 100 | + output_dir="insight_engine_streamlit_reports" | ||
| 101 | + ) | ||
| 102 | + | ||
| 103 | + # 执行研究 | ||
| 104 | + execute_research(query, config) | ||
| 105 | + | ||
| 106 | + | ||
| 107 | +def execute_research(query: str, config: Config): | ||
| 108 | + """执行研究""" | ||
| 109 | + try: | ||
| 110 | + # 创建进度条 | ||
| 111 | + progress_bar = st.progress(0) | ||
| 112 | + status_text = st.empty() | ||
| 113 | + | ||
| 114 | + # 初始化Agent | ||
| 115 | + status_text.text("正在初始化Agent...") | ||
| 116 | + agent = DeepSearchAgent(config) | ||
| 117 | + st.session_state.agent = agent | ||
| 118 | + | ||
| 119 | + progress_bar.progress(10) | ||
| 120 | + | ||
| 121 | + # 生成报告结构 | ||
| 122 | + status_text.text("正在生成报告结构...") | ||
| 123 | + agent._generate_report_structure(query) | ||
| 124 | + progress_bar.progress(20) | ||
| 125 | + | ||
| 126 | + # 处理段落 | ||
| 127 | + total_paragraphs = len(agent.state.paragraphs) | ||
| 128 | + for i in range(total_paragraphs): | ||
| 129 | + status_text.text(f"正在处理段落 {i + 1}/{total_paragraphs}: {agent.state.paragraphs[i].title}") | ||
| 130 | + | ||
| 131 | + # 初始搜索和总结 | ||
| 132 | + agent._initial_search_and_summary(i) | ||
| 133 | + progress_value = 20 + (i + 0.5) / total_paragraphs * 60 | ||
| 134 | + progress_bar.progress(int(progress_value)) | ||
| 135 | + | ||
| 136 | + # 反思循环 | ||
| 137 | + agent._reflection_loop(i) | ||
| 138 | + agent.state.paragraphs[i].research.mark_completed() | ||
| 139 | + | ||
| 140 | + progress_value = 20 + (i + 1) / total_paragraphs * 60 | ||
| 141 | + progress_bar.progress(int(progress_value)) | ||
| 142 | + | ||
| 143 | + # 生成最终报告 | ||
| 144 | + status_text.text("正在生成最终报告...") | ||
| 145 | + final_report = agent._generate_final_report() | ||
| 146 | + progress_bar.progress(90) | ||
| 147 | + | ||
| 148 | + # 保存报告 | ||
| 149 | + status_text.text("正在保存报告...") | ||
| 150 | + agent._save_report(final_report) | ||
| 151 | + progress_bar.progress(100) | ||
| 152 | + | ||
| 153 | + status_text.text("研究完成!") | ||
| 154 | + | ||
| 155 | + # 显示结果 | ||
| 156 | + display_results(agent, final_report) | ||
| 157 | + | ||
| 158 | + except Exception as e: | ||
| 159 | + st.error(f"研究过程中发生错误: {str(e)}") | ||
| 160 | + | ||
| 161 | + | ||
| 162 | +def display_results(agent: DeepSearchAgent, final_report: str): | ||
| 163 | + """显示研究结果""" | ||
| 164 | + st.header("研究结果") | ||
| 165 | + | ||
| 166 | + # 结果标签页(已移除下载选项) | ||
| 167 | + tab1, tab2 = st.tabs(["最终报告", "详细信息"]) | ||
| 168 | + | ||
| 169 | + with tab1: | ||
| 170 | + st.markdown(final_report) | ||
| 171 | + | ||
| 172 | + with tab2: | ||
| 173 | + # 段落详情 | ||
| 174 | + st.subheader("段落详情") | ||
| 175 | + for i, paragraph in enumerate(agent.state.paragraphs): | ||
| 176 | + with st.expander(f"段落 {i + 1}: {paragraph.title}"): | ||
| 177 | + st.write("**预期内容:**", paragraph.content) | ||
| 178 | + st.write("**最终内容:**", paragraph.research.latest_summary[:300] + "..." | ||
| 179 | + if len(paragraph.research.latest_summary) > 300 | ||
| 180 | + else paragraph.research.latest_summary) | ||
| 181 | + st.write("**搜索次数:**", paragraph.research.get_search_count()) | ||
| 182 | + st.write("**反思次数:**", paragraph.research.reflection_iteration) | ||
| 183 | + | ||
| 184 | + # 搜索历史 | ||
| 185 | + st.subheader("搜索历史") | ||
| 186 | + all_searches = [] | ||
| 187 | + for paragraph in agent.state.paragraphs: | ||
| 188 | + all_searches.extend(paragraph.research.search_history) | ||
| 189 | + | ||
| 190 | + if all_searches: | ||
| 191 | + for i, search in enumerate(all_searches): | ||
| 192 | + with st.expander(f"搜索 {i + 1}: {search.query}"): | ||
| 193 | + st.write("**URL:**", search.url) | ||
| 194 | + st.write("**标题:**", search.title) | ||
| 195 | + st.write("**内容预览:**", | ||
| 196 | + search.content[:200] + "..." if len(search.content) > 200 else search.content) | ||
| 197 | + if search.score: | ||
| 198 | + st.write("**相关度评分:**", search.score) | ||
| 199 | + | ||
| 200 | + | ||
| 201 | +if __name__ == "__main__": | ||
| 202 | + main() |
This diff could not be displayed because it is too large.
-
Please register or login to post a comment