Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions examples/agents/bilibili_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import asyncio
import os

# 【新增】引入 OxyRequest 用于工作流函数定义
from oxygent import MAS, Config, oxy, preset_tools, OxyRequest

Config.set_agent_llm_model("default_llm")

oxy_space = [
oxy.HttpLLM(
name="default_llm",
api_key=os.getenv("DEFAULT_LLM_API_KEY"),
base_url=os.getenv("DEFAULT_LLM_BASE_URL"),
model_name=os.getenv("DEFAULT_LLM_MODEL_NAME"),
llm_params={"stream": True},
),
preset_tools.bilibili_tools,
oxy.ReActAgent(
name="bilibili_agent",
desc="A tool that can perform baidu search.",
tools=["bilibili_tools"],
),
]

async def main():
async with MAS(oxy_space=oxy_space) as mas:
await mas.start_web_service(first_query="hello")

if __name__ == "__main__":
asyncio.run(main())
30 changes: 30 additions & 0 deletions examples/agents/video_understanding_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import asyncio
import os

# 【新增】引入 OxyRequest 用于工作流函数定义
from oxygent import MAS, Config, oxy, preset_tools, OxyRequest

Config.set_agent_llm_model("default_llm")

oxy_space = [
oxy.HttpLLM(
name="default_llm",
api_key=os.getenv("DEFAULT_LLM_API_KEY"),
base_url=os.getenv("DEFAULT_LLM_BASE_URL"),
model_name=os.getenv("DEFAULT_LLM_MODEL_NAME"),
llm_params={"stream": True},
),
preset_tools.video_understanding_tools,
oxy.ReActAgent(
name="video_understanding_agent",
desc="A tool can understand the video.",
tools=["video_understanding_tools"],
),
]

async def main():
async with MAS(oxy_space=oxy_space) as mas:
await mas.start_web_service(first_query="hello")

if __name__ == "__main__":
asyncio.run(main())
122 changes: 122 additions & 0 deletions examples/flows/video_download_understanding_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""Workflow-based Reflexion Demo for OxyGent"""

import asyncio
import os

from oxygent import MAS, Config, OxyRequest, oxy

# Set LLM model
Config.set_agent_llm_model("default_llm")


# Reflexion Workflow Core Logic
async def video_analysis_workflow(oxy_request: OxyRequest):
"""
工作流:B站搜索 -> 下载视频 -> 视频理解分析
"""
user_query = oxy_request.get_query(master_level=True)
print(f"== 收到工作流任务: {user_query} ==")

# --- Step 1: 直接调用 Bilibili 搜索 ---
# 要求它只返回一个最相关的视频 URL
bilibili_search_prompt = f"""
请在Bilibili中搜索与以下需求最相关的视频,并只返回最相关的一个视频的完整网页URL,不要返回任何多余文本:
{user_query}
"""

search_resp = await oxy_request.call(
callee="bilibili_agent",
arguments={"query": bilibili_search_prompt}
)

video_url = search_resp.output.strip()
print(f"== Step 1 B站搜索结果: {video_url} ==")

# --- Step 2: 下载视频 ---
download_resp = await oxy_request.call(
callee="bilibili_bangumi_agent",
arguments={"query": video_url}
)

local_video_path = download_resp.output
print(f"== Step 2 视频已下载至: {local_video_path} ==")

# --- Step 3: 视频理解分析 ---
analysis_prompt = f"""
请详细分析位于 '{local_video_path}' 的视频内容,并回答用户的问题:
{user_query}
"""

analysis_resp = await oxy_request.call(
callee="video_understanding_agent",
arguments={"query": analysis_prompt}
)

final_result = analysis_resp.output
print("== Step 3 分析完成 ==")

return final_result


# Define oxy_space
oxy_space = [
# LLM model
oxy.HttpLLM(
name="default_llm",
api_key=os.getenv("DEFAULT_LLM_API_KEY"),
base_url=os.getenv("DEFAULT_LLM_BASE_URL"),
model_name=os.getenv("DEFAULT_LLM_MODEL_NAME"),
llm_params={"temperature": 0.01},
semaphore=4,
timeout=240,
),
# Worker Agent - responsible for generating initial answers
oxy.ReActAgent(
name="worker_agent",
desc="Worker agent responsible for generating initial answers",
llm_model="default_llm",
),
# Reflexion Agent - responsible for evaluating answer quality
oxy.ChatAgent(
name="reflexion_agent",
desc="Reflexion agent responsible for evaluating answer quality and providing improvement suggestions",
llm_model="default_llm",
),
# Math Expert Agent - specifically handles mathematical problems
oxy.ChatAgent(
name="math_expert_agent",
desc="Mathematics expert providing detailed mathematical solutions",
llm_model="default_llm",
),
# Math Checker Agent - checks mathematical solutions
oxy.ChatAgent(
name="math_checker_agent",
desc="Mathematics solution checker verifying the correctness of mathematical solutions",
llm_model="default_llm",
),
# General Reflexion Workflow Agent
preset_tools.bilibili_tools,
oxy.ReActAgent(
name="bilibili_agent",
desc="A tool that can perform baidu search.",
tools=["bilibili_tools"],
),
preset_tools.video_understanding_tools,
oxy.ReActAgent(
name="video_understanding_agent",
desc="A tool can understand the video.",
tools=["video_understanding_tools"],
),
]


async def main():
"""Start Web Service Demo"""
async with MAS(oxy_space=oxy_space) as mas:
await mas.start_web_service(
first_query="Calculate the area of a circle with radius 5."
)


if __name__ == "__main__":
asyncio.run(main())
129 changes: 129 additions & 0 deletions oxygent/preset_tools/bilibili_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import json
import time
import subprocess
from typing import List, Dict
from oxygent.oxy import FunctionHub

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from bs4 import BeautifulSoup

bilibili_tools = FunctionHub(name="bilibili_tools")


def _search_bilibili_html(query: str, max_results: int = 5) -> List[Dict[str, str]]:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from bs4 import BeautifulSoup
import time

chrome_options = Options()
chrome_options.add_argument("--headless=new")
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--window-size=1920,1080")
chrome_options.add_argument("--lang=zh-CN,zh")

driver = webdriver.Chrome(options=chrome_options)

def fetch(url, type_flag):
driver.get(url)
time.sleep(3)
soup = BeautifulSoup(driver.page_source, "html.parser")
results = []

if type_flag == "video":
items = soup.select(".bili-video-card")[:max_results]
for idx, item in enumerate(items, 1):
title_tag = item.select_one("h3")
link_tag = item.select_one("a[href]")
if not (title_tag and link_tag): continue
title = title_tag.get_text(strip=True)
url = link_tag["href"]
url = "https:" + url if url.startswith("//") else url
results.append({
"rank": str(idx), "title": title, "url": url, "type": "video"
})
return results

elif type_flag == "bangumi":
# ✅ 2025 新版 B 站番剧 DOM
selectors = [
".bangumi-card", ".pgc-item", ".pgc-item-wrapper",
".media-card", ".b-subject-item"
]
items = []
for s in selectors:
items = soup.select(s)
if items: break

for idx, item in enumerate(items[:max_results], 1):
title_tag = item.select_one("a[title], .bangumi-title, .title")
if not title_tag: continue
title = title_tag.get("title") or title_tag.get_text(strip=True)
url = title_tag.get("href", "")
url = "https:" + url if url.startswith("//") else url

# ✅ 只保留番剧/纪录片真实入口
if not any(x in url for x in ["bangumi", "/ep", "/ss"]):
continue

results.append({
"rank": str(idx), "title": title, "url": url, "type": "bangumi"
})
return results

# ✅ 搜视频
video_results = fetch(f"https://search.bilibili.com/video?keyword={query}", "video")

# ✅ 搜番剧/纪录片/动画/综艺
bangumi_results = fetch(f"https://search.bilibili.com/bangumi?keyword={query}", "bangumi")

driver.quit()

# ✅ 合并结果,番剧优先
return bangumi_results + video_results


@bilibili_tools.tool(description="Search Bilibili for videos & bangumi (documentaries, anime).")
def search_bilibili(query: str) -> str:
results = _search_bilibili_html(query, max_results=10)
return json.dumps(results, ensure_ascii=False, indent=2)


@bilibili_tools.tool(description="Download normal Bilibili video via yt-dlp.")
def download_bilibili_video(url: str, output_dir: str = "./downloads") -> str:
"""
普通视频下载函数
"""
try:
cmd = [
"yt-dlp",
"-o", f"{output_dir}/%(title)s.%(ext)s",
url
]
subprocess.run(cmd, check=True)
return json.dumps({"status": "success", "message": f"Video saved to {output_dir}"}, ensure_ascii=False)

except subprocess.CalledProcessError as e:
return json.dumps({"status": "error", "message": str(e)}, ensure_ascii=False)

import asyncio

if __name__ == "__main__":
async def main():
query = "人生一串"
print("🔍 正在搜索:", query)
results_json = await search_bilibili(query) # ✅ 加 await
print("✅ 搜索结果:\n", results_json)

results = json.loads(results_json)
if results:
first_video = results[0]["url"]
print(f"\n🎬 开始下载第一个视频: {first_video}")
#print(await download_bilibili_bangumi(first_video)) # ✅ 这里也加 await
else:
print("❌ 未找到视频结果。")

asyncio.run(main())
Loading