agi / app.py
superemewill's picture
Update app.py
e3a0508 verified
raw
history blame
20.7 kB
"""
AI 博客助手 - 多Agent协作系统
使用 LangGraph + Streamlit + OpenAI/Claude 实现
功能:
1. 研究员 Agent:搜索和收集信息
2. 作家 Agent:撰写博客内容
3. 编辑 Agent:审核和优化内容
"""
import streamlit as st
from typing import TypedDict, Annotated, List
import operator
from datetime import datetime
import json
import os
# 检查并安装依赖
try:
from langgraph.graph import StateGraph, END
from langchain_core.messages import HumanMessage, SystemMessage
except ImportError:
st.error("""
⚠️ 缺少必要的依赖包!请运行:
```
pip install langgraph langchain langchain-core langchain-openai
```
""")
st.stop()
# ============= 状态定义 =============
class BlogState(TypedDict):
topic: str # 博客主题
research_notes: str # 研究笔记
draft: str # 草稿
final_blog: str # 最终博客
feedback: str # 编辑反馈
messages: Annotated[List[str], operator.add] # 消息历史
revision_count: int # 修订次数
use_mock: bool # 是否使用模拟模式
# ============= LLM 配置 =============
def get_llm_response(prompt: str, system_prompt: str, use_mock: bool = False):
"""获取 LLM 响应(支持 OpenAI 或模拟模式)"""
if use_mock:
# 模拟模式:返回预设响应
return f"[模拟响应] 基于提示生成的内容:\n{prompt[:100]}..."
try:
# 尝试使用 OpenAI
from langchain_openai import ChatOpenAI
api_key = os.environ.get("OPENAI_API_KEY") or st.session_state.get("openai_key")
if not api_key:
return "[错误] 请配置 OpenAI API Key"
llm = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0.7,
api_key=api_key
)
messages = [
SystemMessage(content=system_prompt),
HumanMessage(content=prompt)
]
response = llm.invoke(messages)
return response.content
except Exception as e:
return f"[LLM 调用失败] {str(e)}\n\n使用模拟模式生成内容..."
# ============= Agent 节点 =============
class ResearcherAgent:
"""研究员 Agent - 负责收集信息"""
def __call__(self, state: BlogState) -> BlogState:
topic = state["topic"]
use_mock = state.get("use_mock", True)
system_prompt = """你是一位专业的研究员,擅长收集和整理信息。
请为给定的博客主题提供详细的研究笔记,包括:
1. 主题背景和重要性
2. 关键概念和术语
3. 目标读者分析
4. 建议的写作角度
5. 相关案例和数据"""
user_prompt = f"""请为以下博客主题进行研究:
主题:{topic}
请提供结构化的研究笔记。"""
if use_mock:
# 模拟响应
research = f"""# {topic} 研究笔记
## 1. 主题背景
{topic}是当前技术领域的重要话题,具有广泛的应用价值和发展前景。
## 2. 关键概念
- 核心概念:{topic}的基本定义和原理
- 技术栈:相关技术和工具
- 应用场景:实际使用案例
## 3. 目标读者
- 初学者:需要入门指导
- 进阶开发者:寻求最佳实践
- 技术决策者:关注价值和ROI
## 4. 建议角度
- 实用性:提供可操作的指南
- 深度:探讨技术细节
- 前瞻性:展望未来趋势
## 5. 参考资料
- 官方文档
- 技术博客
- 开源项目"""
else:
research = get_llm_response(user_prompt, system_prompt, use_mock)
state["research_notes"] = research
state["messages"].append(f"✅ 研究员完成调研:已收集 {topic} 相关信息")
return state
class WriterAgent:
"""作家 Agent - 负责撰写内容"""
def __call__(self, state: BlogState) -> BlogState:
topic = state["topic"]
research = state["research_notes"]
feedback = state.get("feedback", "")
use_mock = state.get("use_mock", True)
if feedback:
system_prompt = """你是一位经验丰富的技术博客作家。
请根据编辑的反馈修改博客内容,确保:
1. 解决所有提出的问题
2. 保持专业和易读
3. 添加必要的示例和说明"""
user_prompt = f"""请修改以下博客内容:
原始主题:{topic}
研究笔记:
{research}
编辑反馈:
{feedback}
请提供修订后的完整博客内容。"""
else:
system_prompt = """你是一位经验丰富的技术博客作家。
请撰写高质量的博客文章,要求:
1. 结构清晰,有引言、正文、总结
2. 内容准确,有深度
3. 包含代码示例(如适用)
4. 语言流畅,易于理解
5. 使用 Markdown 格式"""
user_prompt = f"""请根据以下研究笔记撰写博客:
主题:{topic}
研究笔记:
{research}
请撰写一篇完整的博客文章。"""
if use_mock:
# 模拟响应
draft = f"""# {topic}:深入解析与实践指南
*作者:AI博客助手 | 日期:{datetime.now().strftime('%Y-%m-%d')}*
## 引言
在当今快速发展的技术领域,{topic}正在成为开发者和企业关注的焦点。本文将全面介绍{topic}的核心概念、应用场景和实践经验,帮助读者深入理解并掌握这一技术。
## 什么是{topic}
{topic}是一种[技术描述],它通过[工作原理]来实现[核心功能]。与传统方法相比,{topic}具有以下优势:
- **优势一**:提高效率和性能
- **优势二**:降低复杂度
- **优势三**:增强可维护性
## 核心特性
### 1. 特性一:创新性
{topic}采用了创新的方法来解决传统问题...
### 2. 特性二:可扩展性
系统架构设计灵活,支持水平和垂直扩展...
### 3. 特性三:易用性
提供友好的 API 和工具,降低使用门槛...
## 快速开始
让我们通过一个简单的例子来了解如何使用{topic}
```python
# 示例代码
def example_function():
\"\"\"这是一个{topic}的简单示例\"\"\"
# 初始化
config = {{
'name': '{topic}',
'version': '1.0'
}}
# 执行操作
result = process(config)
return result
# 运行示例
if __name__ == "__main__":
output = example_function()
print(f"结果: {{output}}")
```
## 实践案例
### 案例一:实际应用场景
在[场景描述]中,我们使用{topic}实现了[功能]...
### 案例二:性能优化
通过应用{topic},系统性能提升了[数据]...
## 最佳实践
基于实际经验,以下是使用{topic}的最佳实践:
1. **从简单开始**:先掌握基础功能,再深入高级特性
2. **阅读文档**:官方文档是最好的学习资源
3. **参与社区**:加入技术社区,交流经验
4. **持续学习**:关注最新发展和更新
## 常见问题
**Q1: {topic}适合什么场景?**
A: {topic}特别适合[场景列表]...
**Q2: 如何优化性能?**
A: 可以通过[优化方法]来提升性能...
**Q3: 有哪些注意事项?**
A: 需要注意[注意事项列表]...
## 未来展望
{topic}的发展前景广阔,未来可能会看到:
- 更多的集成和生态系统
- 性能和功能的持续优化
- 更广泛的行业应用
## 总结
通过本文,我们全面了解了{topic}的核心概念、应用实践和最佳经验。{topic}作为一项重要技术,值得每位开发者学习和掌握。
希望这篇文章对你有所帮助。如果有任何问题或建议,欢迎在评论区讨论!
---
**参考资料**
- 官方文档
- 技术社区
- 开源项目
**标签**: {topic}, 技术, 教程, 最佳实践
{f"*本文修订次数: {state['revision_count']}*" if state['revision_count'] > 0 else ""}
"""
else:
draft = get_llm_response(user_prompt, system_prompt, use_mock)
state["draft"] = draft
state["messages"].append(f"✍️ 作家完成{'修订' if feedback else '初稿'}撰写")
return state
class EditorAgent:
"""编辑 Agent - 负责审核和优化"""
def __call__(self, state: BlogState) -> BlogState:
draft = state["draft"]
revision_count = state.get("revision_count", 0)
use_mock = state.get("use_mock", True)
# 质量检查
issues = []
if len(draft) < 500:
issues.append("内容长度不足,需要扩充至至少500字")
if "```" not in draft and ("代码" in state["topic"] or "编程" in state["topic"]):
issues.append("技术文章缺少代码示例")
if "总结" not in draft and "结论" not in draft:
issues.append("缺少总结或结论部分")
if draft.count("#") < 3:
issues.append("文章结构层次不够清晰,建议增加小节")
# 决定是否需要修订
if issues and revision_count < 2:
feedback_text = f"""编辑审核反馈(第{revision_count + 1}次):
需要改进的地方:
{chr(10).join(f'{i+1}. {issue}' for i, issue in enumerate(issues))}
请针对以上问题进行修改,提升文章质量。"""
state["feedback"] = feedback_text
state["revision_count"] = revision_count + 1
state["messages"].append(f"📝 编辑提出修改意见(第{revision_count + 1}次)")
return state
else:
# 批准发布
state["final_blog"] = draft
state["feedback"] = ""
if issues:
state["messages"].append(f"✅ 编辑批准发布(达到最大修订次数,存在{len(issues)}个小问题)")
else:
state["messages"].append("✅ 编辑批准发布(质量优秀)")
return state
# ============= 路由逻辑 =============
def should_continue(state: BlogState) -> str:
"""决定工作流下一步"""
if state.get("final_blog"):
return "end"
elif state.get("feedback"):
return "revise"
else:
return "review"
# ============= 构建工作流 =============
@st.cache_resource
def create_blog_workflow():
"""创建博客生成工作流(缓存)"""
workflow = StateGraph(BlogState)
# 添加节点
workflow.add_node("researcher", ResearcherAgent())
workflow.add_node("writer", WriterAgent())
workflow.add_node("editor", EditorAgent())
# 定义流程
workflow.set_entry_point("researcher")
workflow.add_edge("researcher", "writer")
workflow.add_edge("writer", "editor")
# 条件路由
workflow.add_conditional_edges(
"editor",
should_continue,
{
"end": END,
"revise": "writer",
"review": "editor"
}
)
return workflow.compile()
# ============= Streamlit 界面 =============
def main():
st.set_page_config(
page_title="AI 博客助手",
page_icon="✍️",
layout="wide"
)
# 初始化 session state
if "openai_key" not in st.session_state:
st.session_state.openai_key = ""
st.title("✍️ AI 博客助手")
st.markdown("### 🤖 多 Agent 协作的智能博客生成系统")
# 侧边栏配置
with st.sidebar:
st.header("⚙️ 系统配置")
# API 配置
with st.expander("🔑 API 配置", expanded=False):
api_key = st.text_input(
"OpenAI API Key",
type="password",
value=st.session_state.openai_key,
help="如果不提供,将使用模拟模式"
)
st.session_state.openai_key = api_key
if api_key:
st.success("✅ API Key 已配置")
else:
st.info("💡 未配置 API Key,将使用模拟模式")
st.divider()
# 系统架构说明
st.markdown("""
### 🏗️ 系统架构
**1. 研究员 Agent** 🔍
- 收集主题相关信息
- 分析目标受众
- 提供写作建议
**2. 作家 Agent** ✍️
- 撰写博客内容
- 根据反馈修订
- 保持风格一致
**3. 编辑 Agent** 📝
- 审核内容质量
- 检查结构完整
- 提供改进建议
""")
st.divider()
max_revisions = st.slider(
"最大修订次数",
min_value=0,
max_value=5,
value=2,
help="编辑最多要求修订的次数"
)
st.divider()
# 示例主题
st.markdown("""
### 💡 示例主题
- Python 异步编程
- Docker 容器化
- 微服务架构
- React Hooks
- 机器学习入门
""")
# 主界面
col1, col2 = st.columns([3, 2])
with col1:
st.subheader("📝 输入博客主题")
topic = st.text_input(
"请输入主题",
placeholder="例如:Python装饰器详解、Kubernetes入门指南等",
help="输入您想要撰写的博客主题",
label_visibility="collapsed"
)
# 快捷主题选择
quick_topics = st.pills(
"快速选择:",
["Python异步编程", "React Hooks实战", "Docker容器化", "机器学习基础", "API设计"],
selection_mode="single"
)
if quick_topics:
topic = quick_topics
with col2:
st.subheader("📊 工作流程")
st.code("""
研究员 → 作家 → 编辑
↓ ↓ ↓
调研 撰写 审核
↺ 修订循环
""", language="")
# 生成按钮
generate_btn = st.button(
"🚀 开始生成博客",
type="primary",
use_container_width=True,
disabled=not topic
)
# 生成博客
if generate_btn and topic:
# 确定是否使用模拟模式
use_mock = not bool(st.session_state.openai_key)
if use_mock:
st.info("💡 使用模拟模式生成(未配置 API Key)")
# 初始化状态
initial_state = BlogState(
topic=topic,
research_notes="",
draft="",
final_blog="",
feedback="",
messages=[],
revision_count=0,
use_mock=use_mock
)
# 创建容器
progress_container = st.container()
message_container = st.container()
result_container = st.container()
with progress_container:
progress_bar = st.progress(0, text="初始化...")
status_placeholder = st.empty()
try:
# 创建工作流
app = create_blog_workflow()
# 执行工作流
result = None
step_count = 0
total_steps = 6 # 估计步骤数
with message_container:
st.subheader("📋 执行日志")
log_placeholder = st.empty()
logs = []
for step_result in app.stream(initial_state):
step_count += 1
progress = min(int((step_count / total_steps) * 100), 95)
progress_bar.progress(progress, text=f"执行中... {progress}%")
# 获取当前状态
current_state = list(step_result.values())[0]
# 显示消息
if "messages" in current_state:
new_messages = current_state["messages"]
if new_messages:
logs.extend(new_messages)
log_placeholder.text_area(
"日志",
"\n".join(f"• {msg}" for msg in logs),
height=200,
label_visibility="collapsed"
)
result = current_state
# 完成
progress_bar.progress(100, text="✅ 完成!")
status_placeholder.success("博客生成完成!")
# 显示结果
with result_container:
st.divider()
# 统计信息
col1, col2, col3 = st.columns(3)
with col1:
st.metric("修订次数", result.get("revision_count", 0))
with col2:
st.metric("内容长度", f"{len(result.get('final_blog', ''))} 字符")
with col3:
st.metric("执行步骤", step_count)
# 过程详情
with st.expander("📋 研究笔记", expanded=False):
st.markdown(result.get("research_notes", ""))
if result.get("draft") != result.get("final_blog"):
with st.expander("📄 草稿历史", expanded=False):
st.markdown(result.get("draft", ""))
# 最终博客
st.divider()
st.subheader("📰 最终博客")
final_blog = result.get("final_blog", "")
if final_blog:
# 显示博客
st.markdown(final_blog)
st.divider()
# 操作按钮
col1, col2, col3, col4 = st.columns(4)
with col1:
st.download_button(
label="📥 下载 MD",
data=final_blog,
file_name=f"{topic.replace(' ', '_')}.md",
mime="text/markdown",
use_container_width=True
)
with col2:
export_data = {
"topic": topic,
"content": final_blog,
"metadata": {
"revision_count": result.get("revision_count", 0),
"created_at": datetime.now().isoformat(),
"mode": "mock" if use_mock else "llm"
}
}
st.download_button(
label="📥 下载 JSON",
data=json.dumps(export_data, ensure_ascii=False, indent=2),
file_name=f"{topic.replace(' ', '_')}.json",
mime="application/json",
use_container_width=True
)
with col3:
if st.button("📋 复制内容", use_container_width=True):
st.code(final_blog, language="markdown")
with col4:
if st.button("🔄 重新生成", use_container_width=True):
st.rerun()
else:
st.error("生成失败,请重试")
except Exception as e:
st.error(f"❌ 生成过程出错: {str(e)}")
with st.expander("查看错误详情"):
st.exception(e)
elif generate_btn:
st.warning("⚠️ 请先输入博客主题")
# 底部信息
st.divider()
col1, col2 = st.columns(2)
with col1:
st.markdown("""
### 💡 使用提示
- 输入明确具体的主题
- 配置 API Key 获得更好效果
- 查看执行日志了解过程
- 支持多种格式下载
""")
with col2:
st.markdown("""
### 🔧 技术栈
- **LangGraph**: Agent 协作框架
- **Streamlit**: 交互界面
- **OpenAI**: 大语言模型
- **Python**: 核心开发语言
""")
if __name__ == "__main__":
main()