From 784057207f8b0d3165c1744c65d60a8427ed76d3 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 29 Oct 2025 17:54:06 +0800 Subject: [PATCH 1/2] feat: add websearch tools --- bots/bot_father.ts | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/bots/bot_father.ts b/bots/bot_father.ts index 306129fcb..74b74c724 100644 --- a/bots/bot_father.ts +++ b/bots/bot_father.ts @@ -1,4 +1,4 @@ -import { Agent, Runner, hostedMcpTool, withTrace } from "@openai/agents"; +import { Agent, Runner, hostedMcpTool, withTrace, webSearchTool } from "@openai/agents"; export type WorkflowInput = { input_as_text: string }; @@ -6,8 +6,9 @@ export abstract class BotFather { protected readonly openisleToken = (process.env.OPENISLE_TOKEN ?? "").trim(); protected readonly weatherToken = (process.env.APIFY_API_TOKEN ?? "").trim(); - protected readonly mcp = this.createHostedMcpTool(); + protected readonly openisleMcp = this.createHostedMcpTool(); protected readonly weatherMcp = this.createWeatherMcpTool(); + protected readonly webSearchPreview = this.createWebSearchPreviewTool(); protected readonly agent: Agent; constructor(protected readonly name: string) { @@ -27,7 +28,11 @@ export abstract class BotFather { this.agent = new Agent({ name: this.name, instructions: this.buildInstructions(), - tools: [this.mcp, this.weatherMcp], + tools: [ + this.openisleMcp, + this.weatherMcp, + this.webSearchPreview + ], model: "gpt-4o", modelSettings: { temperature: 0.7, @@ -56,6 +61,19 @@ export abstract class BotFather { ]; } + private createWebSearchPreviewTool() { + return webSearchTool({ + userLocation: { + type: "approximate", + country: undefined, + region: undefined, + city: undefined, + timezone: undefined + }, + searchContextSize: "medium" + }) + } + private createHostedMcpTool() { const token = this.openisleToken; const authConfig = token From f0feb7a45c92616d0e0557d69d6d19c01e8c5cf9 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Wed, 29 Oct 2025 18:01:08 +0800 Subject: [PATCH 2/2] Add daily news bot workflow --- bots/instance/daily_news_bot.ts | 67 +++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 bots/instance/daily_news_bot.ts diff --git a/bots/instance/daily_news_bot.ts b/bots/instance/daily_news_bot.ts new file mode 100644 index 000000000..c2f42004c --- /dev/null +++ b/bots/instance/daily_news_bot.ts @@ -0,0 +1,67 @@ +import { BotFather, WorkflowInput } from "../bot_father"; + +const WEEKDAY_NAMES = ["日", "一", "二", "三", "四", "五", "六"] as const; + +class DailyNewsBot extends BotFather { + constructor() { + super("Daily News Bot"); + } + + protected override getAdditionalInstructions(): string[] { + return [ + "You are DailyNewsBot,专职在 OpenIsle 发布每日新闻速递。", + "始终使用简体中文回复,并以结构化 Markdown 呈现内容。", + "发布内容前务必完成资讯核实:分别通过 web_search 调研 CoinDesk 今日所有要闻、Reuters 今日重点新闻,以及全球 AI 领域的重大进展。", + "整合新闻时,将同源资讯合并,突出影响力、涉及主体与潜在影响,保持语句简洁。", + "所有新闻要点都要附带来源链接,并在括号中标注来源站点名。", + "使用 weather_mcp_server 的 get_current_weather 获取北京、上海、广州、深圳的天气,并在正文中列表展示,格式为“城市:天气描述,最低温~最高温”。", + "正文结尾补充一个行动建议或提醒,帮助读者快速把握重点。", + "确保整篇帖子逻辑清晰:问候 > 新闻分区 > 天气列表 > 总结/提醒。", + "严禁发布超过一篇帖子,create_post 只调用一次。", + ]; + } + + protected override getCliQuery(): string { + const now = new Date(Date.now() + 8 * 60 * 60 * 1000); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, "0"); + const day = String(now.getDate()).padStart(2, "0"); + const weekday = WEEKDAY_NAMES[now.getDay()]; + const dateLabel = `${year}年${month}月${day}日 星期${weekday}`; + const isoDate = `${year}-${month}-${day}`; + const categoryId = Number(process.env.DAILY_NEWS_CATEGORY_ID ?? "1"); + const tagIdsEnv = process.env.DAILY_NEWS_TAG_IDS ?? "1"; + const tagIds = tagIdsEnv + .split(",") + .map((id) => Number(id.trim())) + .filter((id) => !Number.isNaN(id)); + const finalTagIds = tagIds.length > 0 ? tagIds : [1]; + const tagIdsText = `[${finalTagIds.join(", ")}]`; + + return ` +请立即在 https://www.open-isle.com 使用 create_post 发布一篇名为「OpenIsle 每日新闻速递|${dateLabel}」的帖子,并遵循以下要求: +1. 发布类型为 NORMAL,categoryId = ${categoryId},tagIds = ${tagIdsText}。 +2. 正文以简洁问候开头,注明今日日期(${dateLabel})及发布时间(07:00,GMT+8)。 +3. 使用 web_search 工具按以下顺序收集资讯,并在正文中以 Markdown 小节呈现: + - 「全球区块链与加密」:汇总 CoinDesk 在 ${isoDate}(UTC+8 当日)发布的所有重点新闻,提炼 2-3 条核心结论。 + - 「国际财经速览」:汇总 Reuters 当日重点头条,关注宏观经济、市场波动或政策变化。 + - 「AI 行业快讯」:检索全球 AI 领域的重要发布或事件(例如 OpenAI、Google、Meta、国内大模型厂商等)。 +4. 每条新闻采用项目符号,先写结论再给出关键数字或细节,末尾添加来源超链接,格式示例:「**结论** —— 关键细节。(来源:[Reuters](URL))」。 +5. 资讯整理完毕后,调用 weather_mcp_server.get_current_weather,列出北京、上海、广州、深圳今日天气,放置在「城市天气」小节下,每行以“城市:天气描述,最低温~最高温”格式呈现。 +6. 最后一节为「今日提醒」,给出 1-2 条与新闻或天气相关的行动建议。 +7. 若在资讯搜集过程中发现相互矛盾的信息,须在正文中以「⚠️ 风险提示」说明原因及尚待确认的点。 +8. 帖子整体保持在 400 字以内,避免冗长赘述。 +9. 发布完成后,不要再次调用 create_post。 +`.trim(); + } +} + +const dailyNewsBot = new DailyNewsBot(); + +export const runWorkflow = async (workflow: WorkflowInput) => { + return dailyNewsBot.runWorkflow(workflow); +}; + +if (require.main === module) { + dailyNewsBot.runCli(); +}