技术速递|从编写脚本到运营新闻编辑部:基于 Microsoft Copilot Harness 打造一个全天候新闻电台
作者:卢建晖
排版:Alan Wang
想象一下这样的通勤场景:傍晚下班途中,你打开手机上的一个网页,听到两位主播正在播报一档内容连贯的新闻节目。屏幕底部滚动显示着新闻标题和时间戳。点击某条标题,音频就会跳转到对应的新闻内容;点击新闻来源,则会单独打开原始报道。切换到英语,你听到的仍然是基于同一组事实生成的新闻,而不是另一份毫不相关的新闻选集。
这就是 Meridian 24 的构想:一档每半小时更新一次的中国及国际新闻播客,提供中文和英文音频,并通过一个由 HTML、JavaScript 和 CSS3 构建的单页面呈现。
乍一听,这似乎是一个简单的实现方案:搜索新闻,让模型编写播报稿,再将脚本转换成语音。
但真正开始构建后,我们逐渐发现,真正的挑战并不在于让 AI 开口说话,而在于如何让一个小型新闻编辑部以负责任的方式持续运转。
为什么引入 Microsoft Copilot Harness?
第一版新闻脚本需要的是更完善的流程,而不是更漂亮的句子
给模型一堆新闻标题,它就能生成一堆新闻摘要。但这些新闻之间可能缺乏联系。有些搜索结果提供的信息过于有限,模型便可能通过添加评论来填补内容空白。某一家新闻机构也可能因为其报道更容易被检索到,而在节目中占据主导地位。
仅仅再添加一条“让表达听起来更专业”的指令,并不能解决这些问题。
我们需要的是一套编辑方法:收集公开报道、整合相关事件、识别信息缺口、针对缺失信息开展进一步检索、整理有证据支撑的事实,然后才能编写一档由两位主播共同播报、内容连贯的新闻节目。
模型可以成为一名得力的编辑,但一个新闻编辑部仍然需要工作台、资料归档系统、任务分配机制和工作交接流程。
Microsoft Copilot Harness 为这项工作提供了托管式执行基础。Microsoft Foundry 负责运行智能体循环和沙盒环境,协调模型与工具的执行步骤,并支持原生子智能体、文件操作和可复用技能等能力。
开发者因此可以减少搭建新闻编辑部基础设施所花费的时间,将更多精力投入到如何让它真正发挥作用上。
一项在 22:30 启动的制作任务,不应该因为中断而丢失进度
一档新闻节目涉及信息检索、编辑任务、语音合成和音频编码等多个环节。如果服务在已经完成 10 段语音合成后停止运行,重新执行所有步骤就会浪费时间和成本。
这里有两项重要能力,需要明确区分。
Microsoft Copilot Harness 负责运行托管式编辑执行环境。Hosted Agents 的长时间运行恢复能力则为中断任务的重新启动提供基础。至于需要保存哪些进度、如何恢复任务,以及如何避免重复发布,仍然由应用程序决定。
因此,我们没有将整个新闻电台都塞进一个提示词中,也没有将“托管式”理解为“不再需要任何工程开发”。
如何使用 Microsoft Copilot Harness 构建应用?
一个托管式编辑部,以及一个独立的音频制作室
现有的 Hosted 服务已经具备调用 WebIQ、执行语音合成、运行 FFmpeg 以及向 Blob 写入数据的能力。我们没有替换这套服务,而是保留了其中这些确定性的职责,同时使用 Microsoft Copilot Harness 创建了一个独立的 Prompt Agent,专门负责新闻研究和编辑工作。

Microsoft Copilot Harness 负责编辑执行工作,包括研究、整理、任务委派和稿件撰写。Hosted 代码则负责外部 API 调用、验证、语音合成和发布。
这一架构边界也与当前预览版的使用要求有关。在首次公开预览阶段,不能直接将现有智能体切换到另一种 Harness。因此,我们创建了一个新的编辑智能体,同时保留了原有 Hosted Agent 的类型和职责。
下面是项目定义文件中的一段简化示例,省略了完整的指令和自定义新闻工具:
kind: prompt
name: newsradio24-editor
model: gpt-6.1-sol
harness:
type: microsoft_copilot_preview
tools:
- type: microsoft_copilot_toolset_preview
defaultConfig:
enabled: false
configs:
- name: filesystem_read
enabled: true
- name: filesystem_write
enabled: true
- name: shell
enabled: true
- name: subagents
enabled: true
gpt-6.1-sol 是本项目中使用的模型部署名称,并不意味着它在每个项目中都可以使用。请使用已经部署在你所在项目区域、且受到支持的模型。完整配置请参阅 newsroom.yaml,并通过官方文档确认当前预览版支持的字段和工具。
子智能体是幕后编辑,而不是额外的主播
听众听到的始终只有两位主播。在幕后,整个工作流程包含三个逻辑角色:
主编辑负责分配边界明确的任务,并整合各项任务的结果。该项目规定,每次编辑请求最多执行三个子任务。这并不意味着部署了三个独立运行、永久在线的服务,也不意味着每个阶段都会实例化完全相同的三个子智能体。
沙盒相当于编辑的工作台:Shell 和文件工具可以帮助整理 JSON、检查证据 ID 和选题顺序。每段对话都有独立的工作空间,但这个工作空间并不是持久化档案。重要输出必须存储在持久化存储中。
目前,新闻电台通过版本控制的指令和请求级流程来描述其工作方法。项目尚未将这些流程发布为独立的 Foundry Skills。 可复用技能是平台支持的扩展方向,但我们不应该声称当前实现已经使用了这项能力。
让工具提供证据,而不是凭证
编辑智能体通过 collect_news 和 expand_news 请求新闻发现和内容扩展。这两个工具都接受空对象作为输入。服务器负责绑定新闻时间窗口和上下文,而 WebIQ 密钥则保留在 Hosted 服务中。
当外部函数调用到达时,Hosted 代码负责执行搜索,并在同一段对话中返回带有匹配 call_id 的 function_call_output。这样,编辑智能体就可以继续工作,而无需接触任何凭证。
下面这段代码仅展示编辑请求的入口,并非完整的工具执行循环。payload 携带当前阶段的操作流程、证据和输出约定:
import json
import os
from azure.ai.projects.aio import AIProjectClient
from azure.identity.aio import DefaultAzureCredential
async def request_editor(payload):
async with (
DefaultAzureCredential() as credential,
AIProjectClient(
endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
credential=credential,
allow_preview=True,
) as project,
project.get_openai_client(
agent_name="newsradio24-editor",
timeout=900,
max_retries=2,
) as client,
):
conversation = await client.conversations.create()
return await client.responses.create(
conversation=conversation.id,
input=json.dumps(payload, ensure_ascii=False),
)
本项目使用的是 azure-ai-projects 2.9.0。请从服务器端环境中读取你自己的项目终结点。切勿在文章、浏览器代码或沙盒中放入密钥、真实资源组名称或完整的环境配置文件。完整适配器代码请参阅 harness.py。
相较于自行构建的智能体运行框架,它有哪些优势?
作为一名技术布道师,我更倾向于描述它能够减少哪些工作,而不是暗示其他方案无法实现这些能力。
开源框架和自定义系统同样可以支持工具调用、任务委派、状态管理和沙盒环境。两者的区别在于,Microsoft Copilot Harness 将部分执行基础设施交由 Foundry 管理,并将其与平台的智能体定义、身份和工具配置连接起来。

对于新闻电台而言,这种权衡意味着工程团队可以将更多时间投入到证据质量、编辑内容的连贯性和听众体验上。
但这并不意味着报道会自动变得真实,也不意味着可以省略权限配置,或者支持任意模型和工具。如果你需要控制框架、依赖项、进程模型或专用中间件,那么 Hosted Agent 仍然是一个合理的选择。
成本也不会因此消失。模型推理、语音、信息检索、存储、流量和运行时使用都需要纳入预算。具体费用请参考当前所在区域的服务定价和相关协议。
它的优势并不是“所有工程工作都有人替你完成”,而是让你有更清晰的机会,将工程精力投入到真正能够体现应用差异化的地方。
构建全天候新闻电台,哪些技术细节最重要?
先将新闻报道转化为事实,再将事实转化为声音
我们的制作流程不会直接将原始搜索结果发送给语音合成服务,而是先生成明确的中间交付成果:

WebIQ 会从新华社、美联社、法新社和彭博社检索公开报道。我们会核查来源和日期。新闻机构主页、公告以及只有标题的搜索结果,都只是发现新闻线索的起点,不能作为充分的事实证据。
相关报道会被整合为新闻主题,缺失的信息则会转化为有针对性的搜索任务。如果扩展检索后仍然找不到足够的证据,就应该缩短报道或直接舍弃,而不是用主观评论填补内容空白。如果某个板块缺乏足够的事实依据,就不应该发布节目。
“热门”同样需要一个准确的定义:当前实现根据相关性、时效性和跨来源报道覆盖情况进行选题,并不具备经过实际测量的受众热度排行榜。
媒体机构之间的平衡应该建立在证据之上,而不是简单地更换标签
早期节目更倾向于新华社的报道,因为能够检索到的可用报道更多。解决办法并不是把这些报道标记成“美联社”,而是为报道数量相对较少的新闻机构预留候选选题空间,并增加有明确范围限制的扩展搜索。
最终选题最多保留两条中国新闻和四条国际新闻。当选出的可靠非新华社新闻至少有两条时,新华社新闻的占比最多为节目的一半。如果证据不足,最终的媒体来源构成仍然可能不够均衡。页面会如实展示实际使用的新闻来源,而不是虚构四家媒体均有报道的情况。
新闻中立性也不是通过让男女主播交换相反观点来实现的。他们分别补充有引用依据的事实和背景信息,完整讲述一条新闻后再进入下一条,并通过自然的过渡衔接内容,而不是刻意制造争论。
两种语言应该共享同一套事实基础
如果中文和英文分别独立选择新闻,最终就可能变成两档完全不同的节目。
我们的做法是先整理一份有序的事实简报,编写中文正文,再根据同一份简报和中文内容改编英文版本。对于节目开场、AI 语音披露、问候语和礼貌性的结束语,则由确定性代码统一添加,而不是寄希望于模型每次都能记住所有要求。
中文主播使用 MAI Voice 2.1 中的 Wei 和 Mei 音色;英文主播使用 Grant 和 Harper。该新闻电台不设体育新闻板块,也没有第三位主播。
“语音太慢”实际上也是一个时间控制问题
Wei 的中文播报语速需要更快。我们没有在浏览器中加速整档节目,而是在服务器端对 Wei 合成的音轨应用了保留音高的 1.25 倍速处理。Mei 和两位英文主播的语速则保持在 1.0 倍。
这一调整意味着,在计算每条新闻的起始时间、组装音频和进行编码之前,必须重新测量处理后的音轨时长。否则,页面上的“跳转到第二条新闻”按钮就可能定位到错误的句子。
WAV 文件是工作文件。最终的 MP4 文件中包含真正的 AAC 音频,而不是仅仅修改了扩展名的 MP3 文件。完整新闻节目及其每个板块的时长都必须控制在 1,200 秒以内,并以实际音频时长为准。对于超出时长限制的输出,系统会直接拒绝,而不是从一句话中间强行截断。
检查交接记录,而不是相信 AI 声称自己已经完成任务
新闻电台会保存信息收集、选题整理、内容扩展、最终简报、中文脚本和英文脚本等阶段的记录。四个编辑阶段都必须实际执行原生子智能体和 Shell 操作。仅仅返回一句“我已经检查过了”是不够的。
下面通过一段简化代码展示其中两个验证关卡。制作流程会检查各阶段的运行时记录,并在音频制作完成后检查节目时长:
observed = harness.native_tools(response_outputs)
if not {"task", "shell"} <= observed:
raise RuntimeError("Missing native editorial execution")
audio.check_duration(chapters, total_seconds)
原生子任务和 Shell 操作通过平台运行,不能将它们误认为仍然需要 Hosted 代码执行的未解决外部函数调用。该实现依据实际响应事件和 call_id 配对关系进行验证,而不是依赖模型对自身执行情况的评价。
在一次完整的端到端运行中,中文音频的实测时长为 268.1 秒,英文音频为 271.0 秒。验证覆盖了全部六个阶段的记录,以及根据实际 WAV 文件计算出的新闻起始时间。这些只是某一期节目的实际运行结果,并不代表未来的延迟、质量或可用性都能得到保证。
通过这些验证关卡,只能说明系统满足了结构、引用和执行方面的要求,并不能证明内容绝对真实或在语义层面完全中立。人工编辑审核仍然十分重要。
将恢复机制融入数据管理,将发布流程设计成交接机制
已完成的证据、脚本、WAV 文件和状态信息都保存在私有 Blob 存储中。任务恢复时,系统会读取检查点并复用已经合成的音轨,而不是重新执行所有已完成的操作。
只有在中文和英文版本全部完成后,发布租约才会保护目录更新。通过一次写入操作,就可以同时公开两个版本。
定时节目和手动添加的额外节目使用独立目录,因此,手动发布成功不能被当作定时任务成功运行的证明。
我们还遇到了一个非常实际的部署问题:Hosted 服务的实例身份缺少在项目中创建托管式编辑对话的权限。解决办法是在项目范围内,为正确的调用身份分配 Foundry User 角色,并等待权限传播完成。请注意,用户账户拥有相应权限,并不意味着正在运行的智能体也拥有这些权限。
午夜意味着真正的工作交接,而不是重新播放一次
“只保留一天的内容”首先需要明确这一天的定义。我们使用的是当前 Asia/Shanghai 时区的自然日,而不是滚动计算的 24 小时,也不是根据每位访问者所在的本地日期来删除共享 Blob 内容。
每天北京时间午夜,系统都会运行一个独立任务,清理过期目录、公开和私有音频、证据、脚本、WAV 文件及运行时记录,包括快照和已存储的版本。每次节目制作时也会执行一次清理扫描,以处理之前遗漏的清理任务。带日期的文件写入和清理操作共享同一个发布租约,从而避免旧任务在文件删除后重新创建昨天的文件。
即使目录刷新失败,浏览器也会在日期切换时停止播放过期音频。如果当天的第一档节目尚未准备就绪,播放器就会等待。只有在当前新闻电台所属日期内,才允许重复播放最近一档已经完成的节目。
清理操作不会悄悄关闭账户级别的 Azure 软删除保护。如果启用了这项功能,可恢复的文件副本仍然会受到相应策略的约束。已经下载到本地的文件则无法被收回。
最后的细节,都体现在手机屏幕上
前端不需要复杂的框架。一个轻量级单页面就可以实现常驻播放器、新闻标题滚动栏、新闻来源链接、语言切换以及当天节目的按需收听。
点击新闻标题可以跳转到对应音频,点击新闻来源则会打开原始报道。将这两个操作分开,可以避免用户点击时产生歧义。
页面显示的时间应使用听众浏览器所在的时区,而文件路径和清理操作仍然使用北京时间。目录每 45 秒刷新一次。还需要考虑浏览器的自动播放限制,通常用户需要先进行一次点击操作才能开始播放。仅在桌面端进行演示是不够的,还需要实际测试移动端的音频播放、页面滚动、音频定位和时区转换。
结语:一个有用的智能体,不只是给出漂亮的答案
这个项目带来的启发,并不只是 AI 能够自动生成播客节目,而是我们为新闻电台明确了各项职责,建立了持久化的数据存储,并设计了可以检查的工作交接机制。
Microsoft Copilot Harness 为托管式编辑智能体提供工具、工作台和任务委派能力。应用程序代码则负责保障证据、权限、时长、发布和生命周期等边界。
这才是我作为一名技术布道师希望传达的智能体理念:让智能融入真实的工作流程,而不是将整个工作流程隐藏在一个提示词中。
欢迎访问 Meridian 24 页面,了解当前节目并查看交互式数据节点图。该页面只保留当前新闻电台所属日期的内容,并不是一个永久性的节目档案库。
源代码仓库:
https://github.com/kinfey/MSCopilotHarness.NewsPodcast

参考资料
更多推荐



所有评论(0)