superemewill commited on
Commit
e3a0508
·
verified ·
1 Parent(s): 2666e45

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +445 -190
app.py CHANGED
@@ -1,6 +1,6 @@
1
  """
2
  AI 博客助手 - 多Agent协作系统
3
- 使用 LangGraph + Streamlit 实现
4
 
5
  功能:
6
  1. 研究员 Agent:搜索和收集信息
@@ -13,12 +13,19 @@ from typing import TypedDict, Annotated, List
13
  import operator
14
  from datetime import datetime
15
  import json
 
16
 
17
- # 安装依赖提示
18
  try:
19
  from langgraph.graph import StateGraph, END
 
20
  except ImportError:
21
- st.error("请先安装依赖:pip install langgraph langchain langchain-openai")
 
 
 
 
 
22
  st.stop()
23
 
24
  # ============= 状态定义 =============
@@ -30,6 +37,42 @@ class BlogState(TypedDict):
30
  feedback: str # 编辑反馈
31
  messages: Annotated[List[str], operator.add] # 消息历史
32
  revision_count: int # 修订次数
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
 
35
  # ============= Agent 节点 =============
@@ -38,26 +81,49 @@ class ResearcherAgent:
38
 
39
  def __call__(self, state: BlogState) -> BlogState:
40
  topic = state["topic"]
 
41
 
42
- # 模拟研究过程
43
- research = f"""
44
- # {topic} 研究笔记
45
-
46
- ## 关键要点
47
- 1. {topic}是当前热门话题
48
- 2. 主要应用场景包括:数据分析、自动化、AI集成
49
- 3. 技术趋势:向更简单、更强大的方向发展
50
-
51
- ## 目标受众
52
- - 开发者
53
- - 技术管理者
54
- - 对新技术感兴趣的读者
55
-
56
- ## 建议角度
57
- - 实用性:提供具体示例
58
- - 前瞻性:探讨未来发展
59
- - 易读性:避免过度技术化
60
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
  state["research_notes"] = research
63
  state["messages"].append(f"✅ 研究员完成调研:已收集 {topic} 相关信息")
@@ -72,85 +138,155 @@ class WriterAgent:
72
  topic = state["topic"]
73
  research = state["research_notes"]
74
  feedback = state.get("feedback", "")
 
75
 
76
- # 根据反馈决定是修订还是新写
77
  if feedback:
78
- draft = f"""
79
- # {topic}:深入解析与实践指南(修订版)
 
 
 
80
 
81
- ## 引言
82
- 经过编辑反馈,本文进行了以下改进:
 
 
 
 
 
 
83
  {feedback}
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  {research}
86
 
87
- ## 核心内容
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
- ### 1. 什么是{topic}?
90
- {topic}是一个强大的工具/技术,它通过创新的方式解决了传统问题。
91
 
92
- ### 2. 为什么选择{topic}?
93
- - **简单易用**:降低了使用门槛
94
- - **功能强大**:满足复杂需求
95
- - **社区活跃**:持续更新和支持
 
 
96
 
97
- ### 3. 实践示例
98
  ```python
99
  # 示例代码
100
- def example():
101
- print("这是一个{topic}的简单示例")
102
- return "成功"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  ```
104
 
105
- ### 4. 最佳实践
106
- 1. 从简单开始,逐步深入
107
- 2. 关注文档和社区资源
108
- 3. 在实际项目中应用
109
 
110
- ## 总结
111
- {topic}代表了技术发展的新方向,值得每个开发者学习和掌握。
112
 
113
- ---
114
- *本文修订次数: {state['revision_count']}*
115
- """
116
- else:
117
- draft = f"""
118
- # {topic}:深入解析与实践指南
119
 
120
- ## 引言
121
- 在快速发展的技术领域,{topic}正在成为开发者的新宠。本文将全面介绍{topic}的核心概念、应用场景和实践经验。
122
 
123
- {research}
124
 
125
- ## 核心内容
 
 
 
126
 
127
- ### 1. 什么是{topic}?
128
- {topic}是一个革命性的解决方案...
129
 
130
- ### 2. 主要特点
131
- - 特点一:创新性
132
- - 特点二:实用性
133
- - 特点三:可扩展性
134
 
135
- ### 3. 快速上手
136
- ```python
137
- # 快速开始代码
138
- import example
139
- result = example.run()
140
- ```
141
 
142
- ### 4. 进阶技巧
143
- 深入探索{topic}的高级功能...
144
 
145
- ## 结论
146
- 通过本文,您应该对{topic}有了全面的了解。
 
 
 
 
 
 
 
 
 
 
147
 
148
  ---
149
- *作者:AI博客助手 | 日期:{datetime.now().strftime('%Y-%m-%d')}*
 
 
 
 
 
 
 
 
150
  """
 
 
151
 
152
  state["draft"] = draft
153
- state["messages"].append("✍️ 作家完成���稿撰写")
154
 
155
  return state
156
 
@@ -161,23 +297,33 @@ class EditorAgent:
161
  def __call__(self, state: BlogState) -> BlogState:
162
  draft = state["draft"]
163
  revision_count = state.get("revision_count", 0)
 
164
 
165
- # 简单的质量检查
166
  issues = []
167
 
168
  if len(draft) < 500:
169
- issues.append("内容过短,需要扩充")
170
 
171
- if "示例" not in draft and "代码" not in draft:
172
- issues.append("缺少实际示例")
173
 
174
  if "总结" not in draft and "结论" not in draft:
175
- issues.append("缺少总结部分")
 
 
 
176
 
177
  # 决定是否需要修订
178
  if issues and revision_count < 2:
179
- feedback = "需要改进的地方:\n" + "\n".join(f"- {issue}" for issue in issues)
180
- state["feedback"] = feedback
 
 
 
 
 
 
181
  state["revision_count"] = revision_count + 1
182
  state["messages"].append(f"📝 编辑提出修改意见(第{revision_count + 1}次)")
183
  return state
@@ -185,13 +331,18 @@ class EditorAgent:
185
  # 批准发布
186
  state["final_blog"] = draft
187
  state["feedback"] = ""
188
- state["messages"].append("✅ 编辑批准发布")
 
 
 
 
 
189
  return state
190
 
191
 
192
  # ============= 路由逻辑 =============
193
  def should_continue(state: BlogState) -> str:
194
- """决定是否需要继续修订"""
195
  if state.get("final_blog"):
196
  return "end"
197
  elif state.get("feedback"):
@@ -201,8 +352,9 @@ def should_continue(state: BlogState) -> str:
201
 
202
 
203
  # ============= 构建工作流 =============
 
204
  def create_blog_workflow():
205
- """创建博客生成工作流"""
206
 
207
  workflow = StateGraph(BlogState)
208
 
@@ -238,67 +390,123 @@ def main():
238
  layout="wide"
239
  )
240
 
 
 
 
 
241
  st.title("✍️ AI 博客助手")
242
- st.markdown("### 多 Agent 协作的智能博客生成系统")
243
 
244
  # 侧边栏配置
245
  with st.sidebar:
246
- st.header("⚙️ 配置")
247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  st.markdown("""
249
- ### 系统架构
250
- 1. **研究员 Agent** 🔍
251
- - 收集主题相关信息
252
- - 分析目标受众
253
-
254
- 2. **作家 Agent** ✍️
255
- - 撰写博客内容
256
- - 根据反馈修订
257
-
258
- 3. **编辑 Agent** 📝
259
- - 审核内容质量
260
- - 提供改进建议
 
 
 
 
261
  """)
262
 
263
  st.divider()
264
 
265
  max_revisions = st.slider(
266
  "最大修订次数",
267
- min_value=1,
268
  max_value=5,
269
- value=2
 
270
  )
 
 
 
 
 
 
 
 
 
 
 
 
271
 
272
  # 主界面
273
- col1, col2 = st.columns([1, 1])
274
 
275
  with col1:
276
- st.subheader("📝 输入主题")
277
  topic = st.text_input(
278
- "请输入博客主题",
279
- placeholder="例如:Python异步编程、机器学习入门等",
280
- help="输入您想要撰写的博客主题"
 
281
  )
282
 
283
- generate_btn = st.button(
284
- "🚀 生成博客",
285
- type="primary",
286
- use_container_width=True
 
287
  )
 
 
 
288
 
289
  with col2:
290
  st.subheader("📊 工作流程")
291
- st.markdown("""
292
- ```
293
- 研究员 → 作家 → 编辑
294
- ↓ ↓ ↓
295
- 调研 撰写 审核
296
- 修订循环
297
- ```
298
- """)
 
 
 
 
 
 
299
 
300
  # 生成博客
301
  if generate_btn and topic:
 
 
 
 
 
 
302
  # 初始化状态
303
  initial_state = BlogState(
304
  topic=topic,
@@ -307,115 +515,162 @@ def main():
307
  final_blog="",
308
  feedback="",
309
  messages=[],
310
- revision_count=0
 
311
  )
312
 
313
- # 创建进度显示
314
- progress_bar = st.progress(0)
315
- status_text = st.empty()
316
-
317
- # 创建消息容器
318
  message_container = st.container()
 
 
 
 
 
319
 
320
  try:
321
  # 创建工作流
322
  app = create_blog_workflow()
323
 
324
  # 执行工作流
325
- status_text.text("🚀 启动博客生成流程...")
326
- progress_bar.progress(10)
327
-
328
- # 运行并收集结果
329
  result = None
330
  step_count = 0
 
 
 
 
 
 
331
 
332
  for step_result in app.stream(initial_state):
333
  step_count += 1
334
- progress_bar.progress(min(10 + step_count * 20, 90))
 
 
 
 
335
 
336
  # 显示消息
337
- if "messages" in list(step_result.values())[0]:
338
- messages = list(step_result.values())[0]["messages"]
339
- with message_container:
340
- for msg in messages:
341
- st.info(msg)
 
 
 
 
 
342
 
343
- result = list(step_result.values())[0]
344
 
345
- progress_bar.progress(100)
346
- status_text.text("✅ 博客生成完成!")
 
347
 
348
  # 显示结果
349
- st.divider()
350
-
351
- # 显示过程信息
352
- with st.expander("📋 查看研究笔记", expanded=False):
353
- st.markdown(result.get("research_notes", ""))
354
-
355
- with st.expander("📄 查看草稿历史", expanded=False):
356
- st.markdown(result.get("draft", ""))
357
- st.caption(f"修订次数: {result.get('revision_count', 0)}")
358
-
359
- # 显示最终博客
360
- st.subheader("📰 最终博客")
361
- final_blog = result.get("final_blog", "")
362
-
363
- if final_blog:
364
- st.markdown(final_blog)
365
 
366
- # 下载按钮
367
  col1, col2, col3 = st.columns(3)
368
-
369
  with col1:
370
- st.download_button(
371
- label="📥 下载 Markdown",
372
- data=final_blog,
373
- file_name=f"{topic.replace(' ', '_')}.md",
374
- mime="text/markdown"
375
- )
376
-
377
  with col2:
378
- # 导出为 JSON
379
- export_data = {
380
- "topic": topic,
381
- "final_blog": final_blog,
382
- "revision_count": result.get("revision_count", 0),
383
- "created_at": datetime.now().isoformat()
384
- }
385
- st.download_button(
386
- label="📥 下载 JSON",
387
- data=json.dumps(export_data, ensure_ascii=False, indent=2),
388
- file_name=f"{topic.replace(' ', '_')}.json",
389
- mime="application/json"
390
- )
391
-
392
  with col3:
393
- if st.button("📋 复制到剪贴板"):
394
- st.code(final_blog, language="markdown")
395
- else:
396
- st.error("生成失败,请重试")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
 
398
  except Exception as e:
399
- st.error(f"生成过程中出现错误: {str(e)}")
400
- st.exception(e)
 
401
 
402
  elif generate_btn:
403
  st.warning("⚠️ 请先输入博客主题")
404
 
405
  # 底部信息
406
  st.divider()
407
- st.markdown("""
408
- ---
409
- ### 💡 使用提示
410
- - 输入明确的主题以获得更好的结果
411
- - 系统会自动进行多轮优化
412
- - 可以下载生成的博客保存使用
413
 
414
- ### 🔧 技术栈
415
- - **LangGraph**: 多 Agent 协作框架
416
- - **Streamlit**: 交互式界面
417
- - **Python**: 核心开发语言
418
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
 
420
 
421
  if __name__ == "__main__":
 
1
  """
2
  AI 博客助手 - 多Agent协作系统
3
+ 使用 LangGraph + Streamlit + OpenAI/Claude 实现
4
 
5
  功能:
6
  1. 研究员 Agent:搜索和收集信息
 
13
  import operator
14
  from datetime import datetime
15
  import json
16
+ import os
17
 
18
+ # 检查并安装依赖
19
  try:
20
  from langgraph.graph import StateGraph, END
21
+ from langchain_core.messages import HumanMessage, SystemMessage
22
  except ImportError:
23
+ st.error("""
24
+ ⚠️ 缺少必要的依赖包!请运行:
25
+ ```
26
+ pip install langgraph langchain langchain-core langchain-openai
27
+ ```
28
+ """)
29
  st.stop()
30
 
31
  # ============= 状态定义 =============
 
37
  feedback: str # 编辑反馈
38
  messages: Annotated[List[str], operator.add] # 消息历史
39
  revision_count: int # 修订次数
40
+ use_mock: bool # 是否使用模拟模式
41
+
42
+
43
+ # ============= LLM 配置 =============
44
+ def get_llm_response(prompt: str, system_prompt: str, use_mock: bool = False):
45
+ """获取 LLM 响应(支持 OpenAI 或模拟模式)"""
46
+
47
+ if use_mock:
48
+ # 模拟模式:返回预设响应
49
+ return f"[模拟响应] 基于提示生成的内容:\n{prompt[:100]}..."
50
+
51
+ try:
52
+ # 尝试使用 OpenAI
53
+ from langchain_openai import ChatOpenAI
54
+
55
+ api_key = os.environ.get("OPENAI_API_KEY") or st.session_state.get("openai_key")
56
+
57
+ if not api_key:
58
+ return "[错误] 请配置 OpenAI API Key"
59
+
60
+ llm = ChatOpenAI(
61
+ model="gpt-3.5-turbo",
62
+ temperature=0.7,
63
+ api_key=api_key
64
+ )
65
+
66
+ messages = [
67
+ SystemMessage(content=system_prompt),
68
+ HumanMessage(content=prompt)
69
+ ]
70
+
71
+ response = llm.invoke(messages)
72
+ return response.content
73
+
74
+ except Exception as e:
75
+ return f"[LLM 调用失败] {str(e)}\n\n使用模拟模式生成内容..."
76
 
77
 
78
  # ============= Agent 节点 =============
 
81
 
82
  def __call__(self, state: BlogState) -> BlogState:
83
  topic = state["topic"]
84
+ use_mock = state.get("use_mock", True)
85
 
86
+ system_prompt = """你是一位专业的研究员,擅长收集和整理信息。
87
+ 请为给定的博客主题提供详细的研究笔记,包括:
88
+ 1. 主题背景和重要性
89
+ 2. 关键概念和术语
90
+ 3. 目标读者分析
91
+ 4. 建议的写作角度
92
+ 5. 相关案例和数据"""
93
+
94
+ user_prompt = f"""请为以下博客主题进行研究:
95
+ 主题:{topic}
96
+
97
+ 请提供结构化的研究笔记。"""
98
+
99
+ if use_mock:
100
+ # 模拟响应
101
+ research = f"""# {topic} 研究笔记
102
+
103
+ ## 1. 主题背景
104
+ {topic}是当前技术领域的重要话题,具有广泛的应用价值和发展前景。
105
+
106
+ ## 2. 关键概念
107
+ - 核心概念:{topic}的基本定义和原理
108
+ - 技术栈:相关技术和工具
109
+ - 应用场景:实际使用案例
110
+
111
+ ## 3. 目标读者
112
+ - 初学者:需要入门指导
113
+ - 进阶开发者:寻求最佳实践
114
+ - 技术决策者:关注价值和ROI
115
+
116
+ ## 4. 建议角度
117
+ - 实用性:提供可操作的指南
118
+ - 深度:探讨技术细节
119
+ - 前瞻性:展望未来趋势
120
+
121
+ ## 5. 参考资料
122
+ - 官方文档
123
+ - 技术博客
124
+ - 开源项目"""
125
+ else:
126
+ research = get_llm_response(user_prompt, system_prompt, use_mock)
127
 
128
  state["research_notes"] = research
129
  state["messages"].append(f"✅ 研究员完成调研:已收集 {topic} 相关信息")
 
138
  topic = state["topic"]
139
  research = state["research_notes"]
140
  feedback = state.get("feedback", "")
141
+ use_mock = state.get("use_mock", True)
142
 
 
143
  if feedback:
144
+ system_prompt = """你是一位经验丰富的技术博客作家。
145
+ 请根据编辑的反馈修改博客内容,确保:
146
+ 1. 解决所有提出的问题
147
+ 2. 保持专业和易读
148
+ 3. 添加必要的示例和说明"""
149
 
150
+ user_prompt = f"""请修改以下博客内容:
151
+
152
+ 原始主题:{topic}
153
+
154
+ 研究笔记:
155
+ {research}
156
+
157
+ 编辑反馈:
158
  {feedback}
159
 
160
+ 请提供修订后的完整博客内容。"""
161
+ else:
162
+ system_prompt = """你是一位经验丰富的技术博客作家。
163
+ 请撰写高质量的博客文章,要求:
164
+ 1. 结构清晰,有引言、正文、总结
165
+ 2. 内容准确,有深度
166
+ 3. 包含代码示例(如适用)
167
+ 4. 语言流畅,易于理解
168
+ 5. 使用 Markdown 格式"""
169
+
170
+ user_prompt = f"""请根据以下研究笔记撰写博客:
171
+
172
+ 主题:{topic}
173
+
174
+ 研究笔记:
175
  {research}
176
 
177
+ 请撰写一篇完整的博客文章。"""
178
+
179
+ if use_mock:
180
+ # 模拟响应
181
+ draft = f"""# {topic}:深入解析与实践指南
182
+
183
+ *作者:AI博客助手 | 日期:{datetime.now().strftime('%Y-%m-%d')}*
184
+
185
+ ## 引言
186
+
187
+ 在当今快速发展的技术领域,{topic}正在成为开发者和企业关注的焦点。本文将全面介绍{topic}的核心概念、应用场景和实践经验,帮助读者深入理解并掌握这一技术。
188
+
189
+ ## 什么是{topic}?
190
+
191
+ {topic}是一种[技术描述],它通过[工作原理]来实现[核心功能]。与传统方法相比,{topic}具有以下优势:
192
+
193
+ - **优势一**:提高效率和性能
194
+ - **优势二**:降低复杂度
195
+ - **优势三**:增强可维护性
196
+
197
+ ## 核心特性
198
+
199
+ ### 1. 特性一:创新性
200
+ {topic}采用了创新的方法来解决传统问题...
201
 
202
+ ### 2. 特性二:可扩展性
203
+ 系统架构设计灵活,支持水平和垂直扩展...
204
 
205
+ ### 3. 特性三:易用性
206
+ 提供友好的 API 和工具,降低使用门槛...
207
+
208
+ ## 快速开始
209
+
210
+ 让我们通过一个简单的例子来了解如何使用{topic}:
211
 
 
212
  ```python
213
  # 示例代码
214
+ def example_function():
215
+ \"\"\"这是一个{topic}的简单示例\"\"\"
216
+ # 初始化
217
+ config = {{
218
+ 'name': '{topic}',
219
+ 'version': '1.0'
220
+ }}
221
+
222
+ # 执行操作
223
+ result = process(config)
224
+
225
+ return result
226
+
227
+ # 运行示例
228
+ if __name__ == "__main__":
229
+ output = example_function()
230
+ print(f"结果: {{output}}")
231
  ```
232
 
233
+ ## 实践案例
 
 
 
234
 
235
+ ### 案例一:实际应用场景
236
+ 在[场景描述]中,我们使用{topic}实现了[功能]...
237
 
238
+ ### 案例二:性能优化
239
+ 通过应用{topic},系统性能提升了[数据]...
 
 
 
 
240
 
241
+ ## 最佳实践
 
242
 
243
+ 基于实际经验,以下是使用{topic}的最佳实践:
244
 
245
+ 1. **从简单开始**:先掌握基础功能,再深入高级特性
246
+ 2. **阅读文档**:官方文档是最好的学习资源
247
+ 3. **参与社区**:加入技术社区,交流经验
248
+ 4. **持续学习**:关注最新发展和更新
249
 
250
+ ## 常见问题
 
251
 
252
+ **Q1: {topic}适合什么场景?**
253
+ A: {topic}特别适合[场景列表]...
 
 
254
 
255
+ **Q2: 如何优化性能?**
256
+ A: 可以通过[优化方法]来提升性能...
 
 
 
 
257
 
258
+ **Q3: 有哪些注意事项?**
259
+ A: 需要注意[注意事项列表]...
260
 
261
+ ## 未来展望
262
+
263
+ {topic}的发展前景广阔,未来可能会看到:
264
+ - 更多的集成和生态系统
265
+ - 性能和功能的持续优化
266
+ - 更广泛的行业应用
267
+
268
+ ## 总结
269
+
270
+ 通过本文,我们全面了解了{topic}的核心概念、应用实践和最佳经验。{topic}作为一项重要技术,值得每位开发者学习和掌握。
271
+
272
+ 希望这篇文章对你有所帮助。如果有任何问题或建议,欢迎在评论区讨论!
273
 
274
  ---
275
+
276
+ **参考资料**
277
+ - 官方文档
278
+ - 技术社区
279
+ - 开源项目
280
+
281
+ **标签**: {topic}, 技术, 教程, 最佳实践
282
+
283
+ {f"*本文修订次数: {state['revision_count']}*" if state['revision_count'] > 0 else ""}
284
  """
285
+ else:
286
+ draft = get_llm_response(user_prompt, system_prompt, use_mock)
287
 
288
  state["draft"] = draft
289
+ state["messages"].append(f"✍️ 作家完成{'修订' if feedback else '初稿'}撰写")
290
 
291
  return state
292
 
 
297
  def __call__(self, state: BlogState) -> BlogState:
298
  draft = state["draft"]
299
  revision_count = state.get("revision_count", 0)
300
+ use_mock = state.get("use_mock", True)
301
 
302
+ # 质量检查
303
  issues = []
304
 
305
  if len(draft) < 500:
306
+ issues.append("内容长度不足,需要扩充至至少500字")
307
 
308
+ if "```" not in draft and ("代码" in state["topic"] or "编程" in state["topic"]):
309
+ issues.append("技术文章缺少代码示例")
310
 
311
  if "总结" not in draft and "结论" not in draft:
312
+ issues.append("缺少总结或结论部分")
313
+
314
+ if draft.count("#") < 3:
315
+ issues.append("文章结构层次不够清晰,建议增加小节")
316
 
317
  # 决定是否需要修订
318
  if issues and revision_count < 2:
319
+ feedback_text = f"""编辑审核反馈(第{revision_count + 1}次):
320
+
321
+ 需要改进的地方:
322
+ {chr(10).join(f'{i+1}. {issue}' for i, issue in enumerate(issues))}
323
+
324
+ 请针对以上问题进行修改,提升文章质量。"""
325
+
326
+ state["feedback"] = feedback_text
327
  state["revision_count"] = revision_count + 1
328
  state["messages"].append(f"📝 编辑提出修改意见(第{revision_count + 1}次)")
329
  return state
 
331
  # 批准发布
332
  state["final_blog"] = draft
333
  state["feedback"] = ""
334
+
335
+ if issues:
336
+ state["messages"].append(f"✅ 编辑批准发布(达到最大修订次数,存在{len(issues)}个小问题)")
337
+ else:
338
+ state["messages"].append("✅ 编辑批准发布(质量优秀)")
339
+
340
  return state
341
 
342
 
343
  # ============= 路由逻辑 =============
344
  def should_continue(state: BlogState) -> str:
345
+ """决定工作流下一步"""
346
  if state.get("final_blog"):
347
  return "end"
348
  elif state.get("feedback"):
 
352
 
353
 
354
  # ============= 构建工作流 =============
355
+ @st.cache_resource
356
  def create_blog_workflow():
357
+ """创建博客生成工作流(缓存)"""
358
 
359
  workflow = StateGraph(BlogState)
360
 
 
390
  layout="wide"
391
  )
392
 
393
+ # 初始化 session state
394
+ if "openai_key" not in st.session_state:
395
+ st.session_state.openai_key = ""
396
+
397
  st.title("✍️ AI 博客助手")
398
+ st.markdown("### 🤖 多 Agent 协作的智能博客生成系统")
399
 
400
  # 侧边栏配置
401
  with st.sidebar:
402
+ st.header("⚙️ 系统配置")
403
 
404
+ # API 配置
405
+ with st.expander("🔑 API 配置", expanded=False):
406
+ api_key = st.text_input(
407
+ "OpenAI API Key",
408
+ type="password",
409
+ value=st.session_state.openai_key,
410
+ help="如果不提供,将使用模拟模式"
411
+ )
412
+ st.session_state.openai_key = api_key
413
+
414
+ if api_key:
415
+ st.success("✅ API Key 已配置")
416
+ else:
417
+ st.info("💡 未配置 API Key,将使用模拟模式")
418
+
419
+ st.divider()
420
+
421
+ # 系统架构说明
422
  st.markdown("""
423
+ ### 🏗️ 系统架构
424
+
425
+ **1. 研究员 Agent** 🔍
426
+ - 收集主题相关信息
427
+ - 分析目标受众
428
+ - 提供写作建议
429
+
430
+ **2. 作家 Agent** ✍️
431
+ - 撰写博客内容
432
+ - 根据反馈修订
433
+ - 保持风格一致
434
+
435
+ **3. 编辑 Agent** 📝
436
+ - 审核内容质量
437
+ - 检查结构完整
438
+ - 提供改进建议
439
  """)
440
 
441
  st.divider()
442
 
443
  max_revisions = st.slider(
444
  "最大修订次数",
445
+ min_value=0,
446
  max_value=5,
447
+ value=2,
448
+ help="编辑最多要求修订的次数"
449
  )
450
+
451
+ st.divider()
452
+
453
+ # 示例主题
454
+ st.markdown("""
455
+ ### 💡 示例主题
456
+ - Python 异步编程
457
+ - Docker 容器化
458
+ - 微服务架构
459
+ - React Hooks
460
+ - 机器学习入门
461
+ """)
462
 
463
  # 主界面
464
+ col1, col2 = st.columns([3, 2])
465
 
466
  with col1:
467
+ st.subheader("📝 输入博客主题")
468
  topic = st.text_input(
469
+ "请输入主题",
470
+ placeholder="例如:Python装饰器详解、Kubernetes入门指南等",
471
+ help="输入您想要撰写的博客主题",
472
+ label_visibility="collapsed"
473
  )
474
 
475
+ # 快捷主题选择
476
+ quick_topics = st.pills(
477
+ "快速选择:",
478
+ ["Python异步编程", "React Hooks实战", "Docker容器化", "机器学习基础", "API设计"],
479
+ selection_mode="single"
480
  )
481
+
482
+ if quick_topics:
483
+ topic = quick_topics
484
 
485
  with col2:
486
  st.subheader("📊 工作流程")
487
+ st.code("""
488
+ 研究员 → 作家 → 编辑
489
+ ↓ ↓ ↓
490
+ 调研 撰写 审核
491
+ ↺ 修订循环
492
+ """, language="")
493
+
494
+ # 生成按钮
495
+ generate_btn = st.button(
496
+ "🚀 开始生成博客",
497
+ type="primary",
498
+ use_container_width=True,
499
+ disabled=not topic
500
+ )
501
 
502
  # 生成博客
503
  if generate_btn and topic:
504
+ # 确定是否使用模拟模式
505
+ use_mock = not bool(st.session_state.openai_key)
506
+
507
+ if use_mock:
508
+ st.info("💡 使用模拟模式生成(未配置 API Key)")
509
+
510
  # 初始化状态
511
  initial_state = BlogState(
512
  topic=topic,
 
515
  final_blog="",
516
  feedback="",
517
  messages=[],
518
+ revision_count=0,
519
+ use_mock=use_mock
520
  )
521
 
522
+ # 创建容器
523
+ progress_container = st.container()
 
 
 
524
  message_container = st.container()
525
+ result_container = st.container()
526
+
527
+ with progress_container:
528
+ progress_bar = st.progress(0, text="初始化...")
529
+ status_placeholder = st.empty()
530
 
531
  try:
532
  # 创建工作流
533
  app = create_blog_workflow()
534
 
535
  # 执行工作流
 
 
 
 
536
  result = None
537
  step_count = 0
538
+ total_steps = 6 # 估计步骤数
539
+
540
+ with message_container:
541
+ st.subheader("📋 执行日志")
542
+ log_placeholder = st.empty()
543
+ logs = []
544
 
545
  for step_result in app.stream(initial_state):
546
  step_count += 1
547
+ progress = min(int((step_count / total_steps) * 100), 95)
548
+ progress_bar.progress(progress, text=f"执行中... {progress}%")
549
+
550
+ # 获取当前状态
551
+ current_state = list(step_result.values())[0]
552
 
553
  # 显示消息
554
+ if "messages" in current_state:
555
+ new_messages = current_state["messages"]
556
+ if new_messages:
557
+ logs.extend(new_messages)
558
+ log_placeholder.text_area(
559
+ "日志",
560
+ "\n".join(f"• {msg}" for msg in logs),
561
+ height=200,
562
+ label_visibility="collapsed"
563
+ )
564
 
565
+ result = current_state
566
 
567
+ # 完成
568
+ progress_bar.progress(100, text="✅ 完成!")
569
+ status_placeholder.success("博客生成完成!")
570
 
571
  # 显示结果
572
+ with result_container:
573
+ st.divider()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574
 
575
+ # 统计信息
576
  col1, col2, col3 = st.columns(3)
 
577
  with col1:
578
+ st.metric("修订次数", result.get("revision_count", 0))
 
 
 
 
 
 
579
  with col2:
580
+ st.metric("内容长度", f"{len(result.get('final_blog', ''))} 字符")
 
 
 
 
 
 
 
 
 
 
 
 
 
581
  with col3:
582
+ st.metric("执行步骤", step_count)
583
+
584
+ # 过程详情
585
+ with st.expander("📋 研究笔记", expanded=False):
586
+ st.markdown(result.get("research_notes", ""))
587
+
588
+ if result.get("draft") != result.get("final_blog"):
589
+ with st.expander("📄 草稿历史", expanded=False):
590
+ st.markdown(result.get("draft", ""))
591
+
592
+ # 最终博客
593
+ st.divider()
594
+ st.subheader("📰 最终博客")
595
+
596
+ final_blog = result.get("final_blog", "")
597
+
598
+ if final_blog:
599
+ # 显示博客
600
+ st.markdown(final_blog)
601
+
602
+ st.divider()
603
+
604
+ # 操作按钮
605
+ col1, col2, col3, col4 = st.columns(4)
606
+
607
+ with col1:
608
+ st.download_button(
609
+ label="📥 下载 MD",
610
+ data=final_blog,
611
+ file_name=f"{topic.replace(' ', '_')}.md",
612
+ mime="text/markdown",
613
+ use_container_width=True
614
+ )
615
+
616
+ with col2:
617
+ export_data = {
618
+ "topic": topic,
619
+ "content": final_blog,
620
+ "metadata": {
621
+ "revision_count": result.get("revision_count", 0),
622
+ "created_at": datetime.now().isoformat(),
623
+ "mode": "mock" if use_mock else "llm"
624
+ }
625
+ }
626
+ st.download_button(
627
+ label="📥 下载 JSON",
628
+ data=json.dumps(export_data, ensure_ascii=False, indent=2),
629
+ file_name=f"{topic.replace(' ', '_')}.json",
630
+ mime="application/json",
631
+ use_container_width=True
632
+ )
633
+
634
+ with col3:
635
+ if st.button("📋 复制内容", use_container_width=True):
636
+ st.code(final_blog, language="markdown")
637
+
638
+ with col4:
639
+ if st.button("🔄 重新生成", use_container_width=True):
640
+ st.rerun()
641
+ else:
642
+ st.error("生成失败,请重试")
643
 
644
  except Exception as e:
645
+ st.error(f" 生成过程出错: {str(e)}")
646
+ with st.expander("查看错误详情"):
647
+ st.exception(e)
648
 
649
  elif generate_btn:
650
  st.warning("⚠️ 请先输入博客主题")
651
 
652
  # 底部信息
653
  st.divider()
 
 
 
 
 
 
654
 
655
+ col1, col2 = st.columns(2)
656
+
657
+ with col1:
658
+ st.markdown("""
659
+ ### 💡 使用提示
660
+ - 输入明确具体的主题
661
+ - 配置 API Key 获得更好效果
662
+ - 查看执行日志了解过程
663
+ - 支持多种格式下载
664
+ """)
665
+
666
+ with col2:
667
+ st.markdown("""
668
+ ### 🔧 技术栈
669
+ - **LangGraph**: Agent 协作框架
670
+ - **Streamlit**: 交互界面
671
+ - **OpenAI**: 大语言模型
672
+ - **Python**: 核心开发语言
673
+ """)
674
 
675
 
676
  if __name__ == "__main__":