ai_news_generator:基于 CrewAI 与 Cohere Command-R 的多智能体 AI 新闻生成应用
本文以 ai-engineering-hub 仓库中的 ai_news_generator 模块为主体,完整梳理该项目的安装配置、双 Agent 顺序流水线架构、任务输出契约与 Streamlit 前端实现。读完本文,你可以复现一个"输入主题 → 联网检索 → 自动产出带引用的 Markdown 博客"的 CrewAI 多智能体应用,并理解其中每个参数在源码中的实际作用。
项目概览与技术选型
根据 README,该项目的定位是:
This project leverages CrewAI and Cohere's Command-R:7B model to build an AI news generator!
即利用 CrewAI(多智能体编排框架)与 Cohere 的 Command-R(7B 参数规模的对话模型)构建一个 AI 新闻生成器:用户给出一个主题,应用自动完成联网调研并撰写一篇结构化的博客文章。
从源码 ai_news_generator/app.py 的导入部分(L1-L5)可以看到完整的技术栈构成:
import os
import streamlit as st
from crewai import Agent, Task, Crew, LLM
from crewai_tools import SerperDevTool
from dotenv import load_dotenv
- CrewAI:提供
Agent、Task、Crew、LLM四个核心抽象,负责智能体定义、任务编排与 LLM 接入; - crewai-tools 的
SerperDevTool:封装 Serper 搜索引擎 API 的检索工具,供研究类 Agent 调用; - Streamlit:承载 Web 界面,提供主题输入、参数面板与结果下载;
- python-dotenv:通过
load_dotenv()加载本地环境变量(即两个 API Key)。
整体可以概括为:Streamlit 前端 + 顺序执行的双 Agent Crew(研究员 → 写作者)+ Serper 联网搜索 + Cohere Command-R 推理。
环境准备与安装
README 给出的安装步骤如下:
- 获取 API Keys:
- Serper API Key(用于网页搜索);
- Cohere API Key(用于驱动 Command-R 模型)。
- 安装依赖:要求 Python 3.11 或更高版本,然后执行:
pip install crewai crewai-tools
结合 app.py 的实际导入,还有两点 README 未展开、但运行所必需的补充:
-
代码中使用了
streamlit与python-dotenv,因此实际运行前还需要安装这两个包,完整的依赖集合为:crewai、crewai-tools、streamlit、python-dotenv。 -
app.py 在文件顶部调用
load_dotenv(),说明两个 API Key(COHERE_API_KEY与SERPER_API_KEY)应通过环境变量注入,通常的做法是在模块目录下放置.env文件,形如:COHERE_API_KEY=你的Cohere密钥 SERPER_API_KEY=你的Serper密钥其中
COHERE_API_KEY由 CrewAI 的LLM在调用 Cohere 服务时读取,SERPER_API_KEY由SerperDevTool执行搜索时读取。
准备就绪后,进入模块目录运行:
streamlit run ai_news_generator/app.py
应用入口与整体流程
Streamlit 入口配置与页面框架位于 app.py:
st.set_page_config(page_title="AI News Generator", page_icon="📰", layout="wide")
st.title("🤖 AI News Generator, powered by CrewAI and Cohere's Command R7B")
st.markdown("Generate comprehensive blog posts about any topic using AI agents.")
页面采用"侧边栏配置 + 主区域展示"的经典布局:侧边栏负责输入与触发,主区域负责流式展示生成结果并提供下载(详见"Web 界面与运行流程"一节)。
核心生成逻辑封装在 generate_content(topic) 函数中(app.py),其执行链路为:
- 构造
LLM实例(Coherecommand-r,temperature 0.7); - 构造
SerperDevTool搜索工具; - 定义两个
Agent(研究分析师、内容写作者); - 定义两个
Task(调研任务、写作任务); - 用
Crew将 Agent 与 Task 组装成流水线并kickoff,以{"topic": topic}作为输入。
Crew 的构造(app.py)为:
crew = Crew(
agents=[senior_research_analyst, content_writer],
tasks=[research_task, writing_task],
verbose=True
)
return crew.kickoff(inputs={"topic": topic})
这里没有显式指定 process 参数,从源码结构看,Crew 默认采用顺序执行:research_task 的产出作为上下文传入 writing_task,形成"先调研、后写作"的两阶段流水线。verbose=True 会在控制台打印每个 Agent 的推理与工具调用过程,便于调试。kickoff 返回的是 CrewAI 的 CrewOutput 对象,前端随后用其 .raw 属性拿到最终的 Markdown 文本。
LLM 与搜索工具配置
Cohere Command-R 模型实例
模型接入位于 generate_content 开头(app.py):
llm = LLM(
model="command-r",
temperature=0.7
)
model="command-r"即 Cohere 的 Command-R 模型(README 中表述为 "Command-R:7B"),由 CrewAI 的LLM封装类根据模型名前缀路由到 Cohere 提供方,并自动读取COHERE_API_KEY环境变量;temperature=0.7是一个偏"有创造性但可控"的采样温度,兼顾新闻撰写需要的表达灵活性与事实准确性。
Serper 搜索工具
search_tool = SerperDevTool(n_results=10)
SerperDevTool 来自 crewai-tools,底层调用 Serper 的 Google Search API。n_results=10 表示每次搜索返回 10 条结果,作为研究 Agent 的"原始素材池"。该工具仅挂给了研究 Agent,写作者 Agent 并不持有搜索能力——这是后文"职责分离"设计的一部分。
注意:侧边栏中存在一个 temperature 滑杆(见下文 UI 部分),但从源码结构看,
generate_content(topic)的签名只接收topic,函数内LLM的温度被硬编码为 0.7,滑杆值并未传入生成函数。也就是说,当前版本中该滑杆并不实际影响生成结果,若希望其生效,需要将滑杆值作为参数透传给generate_content并在LLM(...)中使用。
Agent 定义:研究员与写作者
Senior Research Analyst(研究分析师)
定义见 app.py:
senior_research_analyst = Agent(
role="Senior Research Analyst",
goal=f"Research, analyze, and synthesize comprehensive information on {topic} from reliable web sources",
backstory="You're an expert research analyst with advanced web research skills. "
"You excel at finding, analyzing, and synthesizing information from "
"across the internet using search tools. ...",
allow_delegation=False,
verbose=True,
tools=[search_tool],
llm=llm
)
各配置项的作用:
- role / goal:
goal中直接以 f-string 注入了本次的{topic},让 Agent 的职责与用户输入强绑定——"从可靠的网络来源研究、分析并综合有关 {topic} 的全面信息"; - backstory:大段人设文本强调其能力画像——擅长用搜索工具检索、区分可靠与不可靠来源、事实核查、交叉验证、给出带引用的研究简报。在 LLM 多智能体系统中,backstory 是塑造 Agent 行为风格的主要 prompt 手段之一;
- tools=[search_tool]:唯一的工具挂载点,即前文
n_results=10的SerperDevTool; - allow_delegation=False:禁止该 Agent 向其他 Agent 委派任务,保证执行路径严格停留在"本 Agent 自己用工具完成调研"上,避免顺序流水线被意外的委派调用打乱;
- verbose=True:打印该 Agent 的每一步推理与工具调用日志。
Content Writer(内容写作者)
定义见 app.py:
content_writer = Agent(
role="Content Writer",
goal="Transform research findings into engaging blog posts while maintaining accuracy",
backstory="You're a skilled content writer specialized in creating "
"engaging, accessible content from technical research. ...",
allow_delegation=False,
verbose=True,
llm=llm
)
与研究员的关键差异:没有挂载任何 tools。它不直接联网,只消费上游研究任务产出的调研简报(Crew 顺序执行时自动作为上下文传入),职责是把技术性研究材料改写成通俗、结构化的博客,同时保留全部事实与引用。这种"检索权与撰写权分离"的结构,让搜索成本集中在第一阶段,第二阶段专注于文本质量。
Task 设计:输出契约与引用规范
两个 Task 的完整定义位于 app.py,它们通过 description 描述工作步骤、通过 expected_output 约定交付物形态。
调研任务(research_task)
description(app.py)规定研究员须对 {topic} 完成四类信息收集——近期动态与新闻、行业趋势与创新、专家观点与分析、统计数据与市场洞察——然后评估来源可信度、核查事实、整理成结构化研究简报,并包含全部引用来源。注意这里的 {topic} 是模板占位符,由 crew.kickoff(inputs={"topic": topic}) 传入的 inputs 在运行时填充(区别于 Agent goal 里的 f-string 立即插值)。
expected_output(app.py)对交付物做了明确的"契约化"约定:
A detailed research report containing:
- Executive summary of key findings
- Comprehensive analysis of current trends and developments
- List of verified facts and statistics
- All citations and links to original sources
- Clear categorization of main themes and patterns
Please format with clear sections and bullet points for easy reference.
即要求报告包含执行摘要、趋势分析、已核实的事实与数据清单、全部引用链接与主题分类,并使用清晰的章节与列表排版。这一约定直接影响第二阶段的质量:写作 Agent 拿到的输入是有结构、有引用的,而非零散文本。
写作任务(writing_task)
description(app.py)要求基于调研简报产出博客,并明确了四条硬约束:
- 将技术性信息转成通俗内容,同时保留研究中的全部事实与引用;
- 结构上包含引人注目的引言、带清晰标题的正文分节、有说服力的结尾;
- 正文引用必须保留
[Source: URL]格式; - 文末附 References 章节。
expected_output(app.py)进一步钉死了 Markdown 排版规范:
A polished blog post in markdown format that:
- ...
- Follows proper markdown formatting, use H1 for the title and H3 for the sub-sections
即标题用 H1、小节用 H3。这个格式约定与 Streamlit 的 st.markdown 渲染直接对应——生成的文章以标准 Markdown 展示,保证下载后也是可直接发布的博客稿。
Web 界面与运行流程
侧边栏:输入与触发
侧边栏逻辑位于 app.py:
with st.sidebar:
st.header("Content Settings")
topic = st.text_area(
"Enter your topic",
height=100,
placeholder="Enter the topic you want to generate content about..."
)
st.markdown("### Advanced Settings")
temperature = st.slider("Temperature", 0.0, 1.0, 0.7)
st.markdown("---")
generate_button = st.button("Generate Content", type="primary", use_container_width=True)
with st.expander("ℹ️ How to use"):
st.markdown("""
1. Enter your desired topic in the text area above
...
""")
界面要素包括:
- 主题输入框:
st.text_area,高度 100px,用于输入任意新闻主题; - Temperature 滑杆:取值范围 0.0~1.0、默认 0.7(如前所述,当前版本该值尚未接入 LLM 参数);
- Generate Content 主按钮:
type="primary"且占满容器宽度,作为唯一触发动作; - 使用帮助折叠面板:内置五步操作说明(输入主题 → 调节参数 → 点击生成 → 等待 → 下载 Markdown)。
主区域:生成、展示与下载
触发后的主流程位于 app.py:
if generate_button:
with st.spinner('Generating content... This may take a moment.'):
try:
result = generate_content(topic)
st.markdown("### Generated Content")
st.markdown(result)
# Add download button
st.download_button(
label="Download Content",
data=result.raw,
file_name=f"{topic.lower().replace(' ', '_')}_article.md",
mime="text/markdown"
)
except Exception as e:
st.error(f"An error occurred: {str(e)}")
几个实现细节值得注意:
- 阻塞式生成 + 加载指示:双 Agent 联网调研加全文写作耗时较长,期间
st.spinner提示"Generating content... This may take a moment."; - 结果渲染:
CrewOutput对象可直接交给st.markdown渲染为富文本; - 一键下载:
st.download_button输出result.raw(最终 Markdown 原文),文件名由主题生成——转小写、空格替换为下划线,形如openai_news_article.md,MIME 类型为text/markdown; - 错误兜底:整个生成过程包在
try/except中,任何异常(如 API Key 缺失、搜索失败)都会以st.error形式展示在页面上,而不是让页面崩溃。
小结与扩展方向
ai_news_generator 用约 160 行代码完整演示了 CrewAI 顺序流水线的最小可用形态:工具挂在研究 Agent 上、写作 Agent 只消费上游产出、Task 用 expected_output 固化交付格式、kickoff 用 inputs 注入模板变量。这套"检索—综合—改写"的分工对新闻、周报、竞品简报等"先查证后成文"的场景是通用骨架。
在理解上述源码结构的基础上,自然的扩展方向包括:
- 将侧边栏 temperature 滑杆的值传入
generate_content,使 UI 参数真正生效; - 为
Crew增加memory配置,让多轮生成之间共享短期记忆; - 调整
process或增加 Agent(如事实核查 Agent),在"写作"之后追加一道引用核验环节; - 将
n_results=10参数化,由用户在侧边栏控制检索深度。
复现时只需按前文的安装步骤准备好 COHERE_API_KEY、SERPER_API_KEY 两个环境变量与四个依赖包,运行 streamlit run ai_news_generator/app.py 即可在浏览器中体验完整流程。更多背景可参考模块内的 README,核心实现见 app.py。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00