首页
/ ai_news_generator:基于 CrewAI 与 Cohere Command-R 的多智能体 AI 新闻生成应用

ai_news_generator:基于 CrewAI 与 Cohere Command-R 的多智能体 AI 新闻生成应用

2026-09-05 16:59:43作者:宣利权Counsellor

本文以 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:提供 AgentTaskCrewLLM 四个核心抽象,负责智能体定义、任务编排与 LLM 接入;
  • crewai-tools 的 SerperDevTool:封装 Serper 搜索引擎 API 的检索工具,供研究类 Agent 调用;
  • Streamlit:承载 Web 界面,提供主题输入、参数面板与结果下载;
  • python-dotenv:通过 load_dotenv() 加载本地环境变量(即两个 API Key)。

整体可以概括为:Streamlit 前端 + 顺序执行的双 Agent Crew(研究员 → 写作者)+ Serper 联网搜索 + Cohere Command-R 推理

环境准备与安装

README 给出的安装步骤如下:

  1. 获取 API Keys
    • Serper API Key(用于网页搜索);
    • Cohere API Key(用于驱动 Command-R 模型)。
  2. 安装依赖:要求 Python 3.11 或更高版本,然后执行:
pip install crewai crewai-tools

结合 app.py 的实际导入,还有两点 README 未展开、但运行所必需的补充:

  • 代码中使用了 streamlitpython-dotenv,因此实际运行前还需要安装这两个包,完整的依赖集合为:crewaicrewai-toolsstreamlitpython-dotenv

  • app.py 在文件顶部调用 load_dotenv(),说明两个 API Key(COHERE_API_KEYSERPER_API_KEY)应通过环境变量注入,通常的做法是在模块目录下放置 .env 文件,形如:

    COHERE_API_KEY=你的Cohere密钥
    SERPER_API_KEY=你的Serper密钥
    

    其中 COHERE_API_KEY 由 CrewAI 的 LLM 在调用 Cohere 服务时读取,SERPER_API_KEYSerperDevTool 执行搜索时读取。

准备就绪后,进入模块目录运行:

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),其执行链路为:

  1. 构造 LLM 实例(Cohere command-r,temperature 0.7);
  2. 构造 SerperDevTool 搜索工具;
  3. 定义两个 Agent(研究分析师、内容写作者);
  4. 定义两个 Task(调研任务、写作任务);
  5. 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 / goalgoal 中直接以 f-string 注入了本次的 {topic},让 Agent 的职责与用户输入强绑定——"从可靠的网络来源研究、分析并综合有关 {topic} 的全面信息";
  • backstory:大段人设文本强调其能力画像——擅长用搜索工具检索、区分可靠与不可靠来源、事实核查、交叉验证、给出带引用的研究简报。在 LLM 多智能体系统中,backstory 是塑造 Agent 行为风格的主要 prompt 手段之一;
  • tools=[search_tool]:唯一的工具挂载点,即前文 n_results=10SerperDevTool
  • 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)

descriptionapp.py)规定研究员须对 {topic} 完成四类信息收集——近期动态与新闻、行业趋势与创新、专家观点与分析、统计数据与市场洞察——然后评估来源可信度、核查事实、整理成结构化研究简报,并包含全部引用来源。注意这里的 {topic} 是模板占位符,由 crew.kickoff(inputs={"topic": topic}) 传入的 inputs 在运行时填充(区别于 Agent goal 里的 f-string 立即插值)。

expected_outputapp.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)

descriptionapp.py)要求基于调研简报产出博客,并明确了四条硬约束:

  1. 将技术性信息转成通俗内容,同时保留研究中的全部事实与引用;
  2. 结构上包含引人注目的引言、带清晰标题的正文分节、有说服力的结尾;
  3. 正文引用必须保留 [Source: URL] 格式
  4. 文末附 References 章节。

expected_outputapp.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_KEYSERPER_API_KEY 两个环境变量与四个依赖包,运行 streamlit run ai_news_generator/app.py 即可在浏览器中体验完整流程。更多背景可参考模块内的 README,核心实现见 app.py

登录后查看全文
热门项目推荐
相关项目推荐