初步实现gui操作
This commit is contained in:
1 parent
1396d652f7
commit
d97e1581a4
56 files changed
+1721
-1586
No files matched your search
@@ -0,0 +1,344 @@
|
||||
<html lang="zh-CN"><head>
|
||||
<meta charset="UTF-8">
|
||||
<title>哔哩哔哩 (゜-゜)つロ 干杯~-bilibili</title>
|
||||
<meta name="description" content="哔哩哔哩(bilibili.com)是国内知名的视频弹幕网站,这里有及时的动漫新番,活跃的ACG氛围,有创意的Up主。大家可以在这里找到许多欢乐。">
|
||||
<meta name="keywords" content="bilibili,哔哩哔哩,哔哩哔哩动画,哔哩哔哩弹幕网,弹幕视频,B站,弹幕,字幕,AMV,MAD,MTV,ANIME,动漫,动漫音乐,游戏,游戏解说,二次元,游戏视频,ACG,galgame,动画,番组,新番,初音,洛天依,vocaloid,日本动漫,国产动漫,手机游戏,网络游戏,电子竞技,ACG燃曲,ACG神曲,追新番,新番动漫,新番吐槽,巡音,镜音双子,千本樱,初音MIKU,舞蹈MMD,MIKUMIKUDANCE,洛天依原创曲,洛天依翻唱曲,洛天依投食歌,洛天依MMD,vocaloid家族,OST,BGM,动漫歌曲,日本动漫音乐,宫崎骏动漫音乐,动漫音乐推荐,燃系mad,治愈系mad,MAD MOVIE,MAD高燃">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="spm_prefix" content="333.1007">
|
||||
<meta name="referrer" content="no-referrer-when-downgrade">
|
||||
<meta name="applicable-device" content="pc">
|
||||
<meta http-equiv="Cache-Control" content="no-transform">
|
||||
<meta http-equiv="Cache-Control" content="no-siteapp">
|
||||
<meta name="server_render" content="is_server_render">
|
||||
<link rel="dns-prefetch" href="//s1.hdslb.com">
|
||||
<link rel="apple-touch-icon" href="https://i0.hdslb.com/bfs/static/jinkela/long/images/512.png">
|
||||
<link rel="shortcut icon" href="https://i0.hdslb.com/bfs/static/jinkela/long/images/favicon.ico">
|
||||
<link rel="canonical" href="https://www.bilibili.com/">
|
||||
<link rel="alternate" media="only screen and (max-width: 640px)" href="https://m.bilibili.com">
|
||||
<script>window._BiliGreyResult={"method":"base","grayVersion":"249026"}</script><script type="text/javascript">window.__bili_graystyle_fetched__=true</script>
|
||||
<script type="text/javascript">
|
||||
;(function () {
|
||||
if (document.querySelector('meta[name=server_render]')) {
|
||||
return
|
||||
}
|
||||
var ua = window.navigator.userAgent,
|
||||
agents = ['Android', 'Phone', 'SymbianOS', 'iPod'],
|
||||
isPC = true
|
||||
if (/\sVR\s/g.test(ua)) return
|
||||
for (var i = 0, len = agents.length; i < len; i++) {
|
||||
if (ua.indexOf(agents[i]) > 0) {
|
||||
isPC = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!isPC) {
|
||||
window.location.href = window.location.href.replace('www', 'm')
|
||||
}
|
||||
})()
|
||||
</script>
|
||||
<script src="//s1.hdslb.com/bfs/seed/jinkela/short/bmg/register/fallback.js"></script><script src="//s1.hdslb.com/bfs/seed/jinkela/kv-sdk/index.js"></script>
|
||||
<!--preload-fetch-->
|
||||
|
||||
<script type="text/javascript">
|
||||
window.__NANO_VERSION_HASH__ = "b237bb82"
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
if (!window.abtest) {
|
||||
window.abtest = {
|
||||
'b_ut': '',
|
||||
'home_version': 'V8',
|
||||
'in_new_ab': true,
|
||||
'ab_version': {"for_ai_home_version":"V8","rcmd_timeout_config":"550","home_performance_opt":"ssr_fetch_opt","force_go_download":"YES"},
|
||||
'ab_split_num': {"for_ai_home_version":54,"rcmd_timeout_config":49,"home_performance_opt":49,"force_go_download":54},
|
||||
'uniq_page_id': '707564570583',
|
||||
'is_modern': true,
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
window.spmReportData = {}
|
||||
window.reportConfig = {
|
||||
sample: 1,
|
||||
msgObjects: 'spmReportData',
|
||||
errorTracker: true
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="//s1.hdslb.com/bfs/seed/log/report/log-reporter.js"></script>
|
||||
<script>
|
||||
// bili-mirror 告警上报
|
||||
const isBilibili = location.host.includes('bilibili.com')
|
||||
const isSpider = !(navigator && navigator.userAgent) || /bot|spider/i.test(navigator.userAgent)
|
||||
if (isBilibili && !isSpider) {
|
||||
class MirrorErrorFilterPlugin {
|
||||
static get errorFilterList() {
|
||||
return ['extension', 'log-reporter', 'static.geetest.com']
|
||||
}
|
||||
static get rejectionFilterList() {
|
||||
return ['extension', 'aborted', 'failed', 'timeout', 'network', 'log-reporter']
|
||||
}
|
||||
static get resourceFilterList() {
|
||||
return ['extension']
|
||||
}
|
||||
// static cookieMidRegExp = /DedeUserID=(.+?);/
|
||||
// static notBiliResourceRegExp = /^(?!.*\.hdslb\.com|.*\.bilibili\.com).*$/
|
||||
checkInWhitelist(list, message, stack) {
|
||||
if (!list || !list.length) return false
|
||||
const isMsgMatch = message && list.some(str => {
|
||||
return message.toLowerCase().includes(str)
|
||||
})
|
||||
const isStackMatch = stack && list.some(str => {
|
||||
return stack.toLowerCase().includes(str)
|
||||
})
|
||||
return isMsgMatch || isStackMatch
|
||||
}
|
||||
mirrorHandleBefore(type, data) {
|
||||
return new Promise(resolve => {
|
||||
if (type === 'error') {
|
||||
const isMatch = this.checkInWhitelist(
|
||||
MirrorErrorFilterPlugin.errorFilterList,
|
||||
data && data.message ? data.message : '',
|
||||
data && data.stack ? data.stack : ''
|
||||
)
|
||||
return resolve(!isMatch)
|
||||
}
|
||||
else if (type === 'unhandledrejection') {
|
||||
const isMatch = this.checkInWhitelist(
|
||||
MirrorErrorFilterPlugin.rejectionFilterList,
|
||||
data && data.message ? data.message : '',
|
||||
data && data.stack ? data.stack : ''
|
||||
)
|
||||
return resolve(!isMatch)
|
||||
}
|
||||
else if (type === 'resource') {
|
||||
const cookieMid = MirrorErrorFilterPlugin.cookieMidRegExp.exec(document.cookie)
|
||||
MirrorErrorFilterPlugin.cookieMidRegExp.lastIndex = 0
|
||||
if (!cookieMid) {
|
||||
return resolve(false)
|
||||
}
|
||||
const isNotBiliResource = MirrorErrorFilterPlugin.notBiliResourceRegExp.test(data && data.message ? data.message : '')
|
||||
MirrorErrorFilterPlugin.notBiliResourceRegExp.lastIndex = 0
|
||||
if (isNotBiliResource) {
|
||||
return resolve(false)
|
||||
}
|
||||
const isMatch = this.checkInWhitelist(
|
||||
MirrorErrorFilterPlugin.resourceFilterList,
|
||||
data && data.message ? data.message : '',
|
||||
data && data.stack ? data.stack : ''
|
||||
)
|
||||
return resolve(!isMatch)
|
||||
}
|
||||
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
mirrorHandleAfter() {
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
MirrorErrorFilterPlugin.cookieMidRegExp = /DedeUserID=(.+?);/
|
||||
MirrorErrorFilterPlugin.notBiliResourceRegExp = /^(?!.*\.hdslb\.com|.*\.bilibili\.com).*$/
|
||||
window.__MIRROR_CONFIG__ = {
|
||||
origin: 'main',
|
||||
module: 'home-page',
|
||||
spmId: '333.1007',
|
||||
plugins: new MirrorErrorFilterPlugin(),
|
||||
config: {
|
||||
isAutoInit: true,
|
||||
whiteScreen: {
|
||||
maxLoop: 10,
|
||||
checkDom: ['#app', '.bili-feed4-layout', '.bili-mini-mask', '.geetest_panel_ghost', '#bewly', '.bewly-design', '.home-redesign-base', '.bilibili-helper-dark-mode', '.bili-wrapper'],
|
||||
callback: status => {
|
||||
console.log('白屏检测是否正常', status)
|
||||
},
|
||||
isSkeleton: false
|
||||
},
|
||||
ignoreVersion: '2.0.0',
|
||||
ignorePageVersion: [108288]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script src="//s1.hdslb.com/bfs/seed/jinkela/short/b-mirror/biliMirror.umd.mini.js"></script>
|
||||
<script type="text/javascript">
|
||||
/** 性能上报相关 */
|
||||
function fsrCb() {
|
||||
const t = new Date().getTime()
|
||||
if (window.performance && window.performance.timing) {
|
||||
window.performance.timing.firstscreenfinish = t
|
||||
}
|
||||
}
|
||||
window.__HOME_PAGE_PERFORMANCE__ = {} // 赋值,防止后面使用存在判空报错
|
||||
// 2024首页性能打点上报 start
|
||||
var isHeaderBannerReported = false
|
||||
function headerBannerLoaded() {
|
||||
if (!isHeaderBannerReported) {
|
||||
isHeaderBannerReported = true
|
||||
window.__HOME_PAGE_PERFORMANCE__.header_static_img_load = +new Date() - window.performance.timing.navigationStart
|
||||
if (window.__MIRROR_REPORT__ && typeof window.__MIRROR_REPORT__.customPerformanceQuota === 'function') {
|
||||
window.__MIRROR_REPORT__.customPerformanceQuota({
|
||||
name: 'header_static_img_load',
|
||||
value: window.__HOME_PAGE_PERFORMANCE__.header_static_img_load,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
var isFirstSwipeLoadReported = false
|
||||
function firstSwipeLoaded(index) {
|
||||
if (+index <= 1 && !isFirstSwipeLoadReported) {
|
||||
isFirstSwipeLoadReported = true
|
||||
window.__HOME_PAGE_PERFORMANCE__.swipe_img_load = +new Date() - window.performance.timing.navigationStart
|
||||
if (window.__MIRROR_REPORT__ && typeof window.__MIRROR_REPORT__.customPerformanceQuota === 'function') {
|
||||
window.__MIRROR_REPORT__.customPerformanceQuota({
|
||||
name: 'swipe_img_load',
|
||||
value: window.__HOME_PAGE_PERFORMANCE__.swipe_img_load,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
var isFirstVideoCardImgLoadReported = false
|
||||
function firstVideoCardImgLoaded() {
|
||||
if (!isFirstVideoCardImgLoadReported) {
|
||||
isFirstVideoCardImgLoadReported = true
|
||||
window.__HOME_PAGE_PERFORMANCE__.videocard_img_load = +new Date() - window.performance.timing.navigationStart
|
||||
if (window.__MIRROR_REPORT__ && typeof window.__MIRROR_REPORT__.customPerformanceQuota === 'function') {
|
||||
window.__MIRROR_REPORT__.customPerformanceQuota({
|
||||
name: 'videocard_img_load',
|
||||
value: window.__HOME_PAGE_PERFORMANCE__.videocard_img_load,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2024首页性能打点上报 end
|
||||
// 图片降级使用
|
||||
function imgOnError(img) {
|
||||
typeof window.imgAutoFallbackOnError === 'function' && window.imgAutoFallbackOnError(img)
|
||||
}
|
||||
// 图片降级使用
|
||||
function imgOnLoad(img) {
|
||||
typeof window.imgAutoFallbackOnLoad === 'function' && window.imgAutoFallbackOnLoad(img)
|
||||
}
|
||||
function lqipCb(img) {
|
||||
var lqip =
|
||||
img && img.parentNode && img.parentNode.querySelector('.lqip')
|
||||
if (lqip) {
|
||||
lqip.classList.add('is-active')
|
||||
}
|
||||
}
|
||||
window.page_load_time = Date.now()
|
||||
if (history.scrollRestoration) {
|
||||
history.scrollRestoration = 'manual'
|
||||
}
|
||||
</script>
|
||||
|
||||
<link rel="stylesheet" href="//s1.hdslb.com/bfs/static/shanks/laputa-home/assets/index-88ff9ece.css">
|
||||
<script type="module">import.meta.url;import("_").catch(()=>1);async function* g(){};if(location.protocol!="file:"){window.__vite_is_modern_browser=true}</script>
|
||||
<script type="module">!function(){if(window.__vite_is_modern_browser)return;console.warn("vite: loading legacy chunks, syntax error above and the same error below should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}();</script>
|
||||
<script src="//s1.hdslb.com/bfs/seed/jinkela/short/reporter-pb/index.js"></script><style>
|
||||
.bytj55s3395dsk77, .bcn2oawtzhz1dpb8 {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding-top: var(--cover-radio);
|
||||
background-color: var(--graph_bg_regular);
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
color: var(--text4);
|
||||
white-space: nowrap;
|
||||
display: block!important;
|
||||
}
|
||||
.bytj55s3395dsk77::before, .bcn2oawtzhz1dpb8::before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -100%);
|
||||
content: '该内容被AdGuard/AdBlock类插件屏蔽';
|
||||
}
|
||||
.bytj55s3395dsk77::after, .bcn2oawtzhz1dpb8::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
content: '请检查插件以恢复正常内容展示';
|
||||
}
|
||||
</style><script id="bs_url_12298599294" type="text/JavaScript" src="//s1.hdslb.com/bfs/static/jinkela/long/unify-http-request/forticket.min.js"></script><script id="bs_url_5507829314" type="text/JavaScript" src="//s1.hdslb.com/bfs/seed/jinkela/short/watchlater-pip/watchlater-pip.umd.js"></script></head>
|
||||
<body style="background-color: var(--bg3)" class="win">
|
||||
<div class="browser-tip"></div>
|
||||
<script>
|
||||
if (
|
||||
window.abtest &&
|
||||
window.abtest.is_modern === false
|
||||
) {
|
||||
document.querySelector('.browser-tip').innerHTML =
|
||||
'<div class="title">当前浏览器版本较低,为保证您的使用体验,建议使用最新版本的浏览器访问。</div>'
|
||||
}
|
||||
if (/(Mac|iPhone|iPod|iPad)/i.test(window.navigator.platform)) {
|
||||
document.body.classList.add('mac')
|
||||
} else {
|
||||
document.body.classList.add('win')
|
||||
}
|
||||
</script>
|
||||
<div id="app"><!--[--><!--[--><div class="bili-video-card__wrap __scale-wrap" data-v-a6998ac5=""><a href="//cm.bilibili.com" data-target-url="**" data-v-a6998ac5=""></a></div><!----><!--]--><div class="bili-feed4"><!--[--><div class="bili-header large-header"><div class="bili-header__bar"><ul class="left-entry"><li><a href="//www.bilibili.com" class="entry-title"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" class="zhuzhan-icon"><path fill-rule="evenodd" clip-rule="evenodd" d="M3.73252 2.67094C3.33229 2.28484 3.33229 1.64373 3.73252 1.25764C4.11291 0.890684 4.71552 0.890684 5.09591 1.25764L7.21723 3.30403C7.27749 3.36218 7.32869 3.4261 7.37081 3.49407H10.5789C10.6211 3.4261 10.6723 3.36218 10.7325 3.30403L12.8538 1.25764C13.2342 0.890684 13.8368 0.890684 14.2172 1.25764C14.6175 1.64373 14.6175 2.28484 14.2172 2.67094L13.364 3.49407H14C16.2091 3.49407 18 5.28493 18 7.49407V12.9996C18 15.2087 16.2091 16.9996 14 16.9996H4C1.79086 16.9996 0 15.2087 0 12.9996V7.49406C0 5.28492 1.79086 3.49407 4 3.49407H4.58579L3.73252 2.67094ZM4 5.42343C2.89543 5.42343 2 6.31886 2 7.42343V13.0702C2 14.1748 2.89543 15.0702 4 15.0702H14C15.1046 15.0702 16 14.1748 16 13.0702V7.42343C16 6.31886 15.1046 5.42343 14 5.42343H4ZM5 9.31747C5 8.76519 5.44772 8.31747 6 8.31747C6.55228 8.31747 7 8.76519 7 9.31747V10.2115C7 10.7638 6.55228 11.2115 6 11.2115C5.44772 11.2115 5 10.7638 5 10.2115V9.31747ZM12 8.31747C11.4477 8.31747 11 8.76519 11 9.31747V10.2115C11 10.7638 11.4477 11.2115 12 11.2115C12.5523 11.2115 13 10.7638 13 10.2115V9.31747C13 8.76519 12.5523 8.31747 12 8.31747Z" fill="currentColor"></path></svg><span>首页</span></a></li><!----><!--[--><!--[--><li class="v-popover-wrap"><!--[--><a href="//www.bilibili.com/anime/" target="_blank" class="default-entry"><span>番剧</span></a><!--]--><!----></li><!----><!--]--><!--[--><li class="v-popover-wrap"><!--[--><a href="//live.bilibili.com" target="_blank" class="default-entry"><span>直播</span></a><!--]--><!----></li><!----><!--]--><!--[--><li class="v-popover-wrap"><!--[--><a href="//game.bilibili.com/platform" target="_blank" class="default-entry"><span>游戏中心</span></a><!--]--><!----></li><!----><!--]--><!--[--><li class="v-popover-wrap"><!--[--><a href="//show.bilibili.com/platform/home.html?msource=pc_web" target="_blank" class="default-entry"><span>会员购</span></a><!--]--><!----></li><!----><!--]--><!--[--><li class="v-popover-wrap"><!--[--><a href="//manga.bilibili.com?from=bill_top_mnav" target="_blank" class="default-entry"><span>漫画</span></a><!--]--><!----></li><!----><!--]--><!--[--><li class="v-popover-wrap"><!--[--><a href="//www.bilibili.com/match/home/" target="_blank" class="default-entry"><span>赛事</span></a><!--]--><!----></li><!----><!--]--><!--[--><!----><!----><!--]--><!--[--><!----><li class="v-popover-wrap left-loc-entry"><div><a class="loc-entry loc-moveclip" loc-id="2954" href="https://www.bilibili.com/bangumi/play/ss134633" target="_blank"><div class="loc-mc-box"><p class="loc-mc-box__text loc-mc-box__text--up">心间错</p><p class="loc-mc-box__pic"><img src="//i0.hdslb.com/bfs/banner/16ce15a437603d0d418b578b4523a0a88ad1a7f5.png" alt="图片"></p><p class="loc-mc-box__text loc-mc-box__text--down">心间错</p></div></a></div><!----></li><!--]--><!--]--><li class="v-popover-wrap"><!--[--><a href="//app.bilibili.com" target="_blank" class="download-entry download-client-trigger"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" class="download-client-trigger__icon"><path d="M7.23181 8.65895V1.75796C7.23181 1.33935 7.57582 1 8.00018 1C8.42453 1 8.76854 1.33935 8.76854 1.75796V8.67097L9.98589 7.47009C10.286 7.17409 10.7725 7.17409 11.0725 7.47009C11.3726 7.7661 11.3726 8.24601 11.0725 8.54201L8.54958 11.0308C8.24952 11.3268 7.76302 11.3268 7.46295 11.0308L4.94002 8.54201C4.63995 8.24601 4.63995 7.7661 4.94002 7.47009C5.24008 7.17409 5.72658 7.17409 6.02665 7.47009L7.23181 8.65895Z" fill="currentColor"></path><path d="M3.48023 4.29936C2.40686 4.29936 1.53672 5.15772 1.53672 6.21656V11.5669C1.53672 12.6257 2.40686 13.4841 3.48023 13.4841H12.5198C13.5931 13.4841 14.4633 12.6257 14.4633 11.5669V6.21656C14.4633 5.15772 13.5931 4.29936 12.5198 4.29936H11.6158C11.1915 4.29936 10.8475 3.96001 10.8475 3.5414C10.8475 3.12279 11.1915 2.78344 11.6158 2.78344H12.5198C14.4418 2.78344 16 4.3205 16 6.21656V11.5669C16 13.4629 14.4418 15 12.5198 15H3.48023C1.55815 15 0 13.4629 0 11.5669V6.21656C0 4.3205 1.55815 2.78344 3.48023 2.78344H4.38418C4.80853 2.78344 5.15254 3.12279 5.15254 3.5414C5.15254 3.96001 4.80853 4.29936 4.38418 4.29936H3.48023Z" fill="currentColor"></path></svg><span>下载客户端</span><span class="new-icon"></span></a><!--]--><!----></li></ul><div class="center-search-container offset-center-search"><div class="center-search__bar"><form id="nav-searchform" class="" style="border-radius:8px 8px 8px 8px;"><div class="nav-search-content"><input clLine truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-0fe7dc61=""><!--]--><!--]--></picture><div class="animated-banner"></div><div class="header-banner__inner"><a href="//www.bilibili.com" class="inner-logo"><img class="logo-img" alt="B站 b站" width="162" height="78" src="//i1.hdslb.com/bfs/archive/87a5d03581e326f4f818cab3212ce471d1f6a064.png"></a><!----></div><div class="taper-line"></div><!----></div><div class="bili-header__channel" data-v-b65933a6=""><div class="channel-icons" data-v-b65933a6=""><!--[--><!----><a class="channel-icons__item" href="//t.bilibili.com" target="_blank" data-v-5913be20=""><div class="icon-bg icon-bg__dynamic" data-v-5913be20=""><svg width="22" height="23" viewBox="0 0 22 23" fill="none" xmlns="http://www.w3.org/2000/svg" class="icon-bg--icon" data-v-5913be20=""><path d="M6.41659 15.625C3.88528 15.625 1.83325 13.7782 1.83325 11.5H10.9999C10.9999 13.7782 8.94789 15.625 6.41659 15.625Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15.125 16.0827C15.125 18.614 13.2782 20.666 11 20.666L11 11.4993C13.2782 11.4993 15.125 13.5514 15.125 16.0827Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M6.875 6.91667C6.875 9.44797 8.72183 11.5 11 11.5L11 2.33333C8.72182 2.33333 6.875 4.38536 6.875 6.91667Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15.5833 7.375C13.052 7.375 11 9.22183 11 11.5H20.1667C20.1667 9.22183 18.1146 7.375 15.5833 7.375Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></div><!----><span class="icon-title" data-v-5913be20="">动态</span></a><a href="//www.bilibili.com/v/popular/all" target="_blank" class="channel-icons__item"><div class="icon-bg icon-bg__popular"><svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg" class="icon-bg--icon"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.89054 17.272L4.89277 17.2742C6.49674 18.8782 8.66472 19.7888 10.9624 19.7888C13.2503 19.7888 15.2113 19.0539 16.6107 17.6108L16.6108 17.6108L16.6128 17.6086C18.0002 16.1345 18.7835 14.182 18.7421 12.1819C18.7852 11.3835 18.6916 9.36321 17.4088 6.75488L17.4082 6.7537C17.209 6.35523 16.8163 6.06598 16.3391 5.96993C15.8904 5.87103 15.4021 6.01997 15.061 6.35741C14.9094 6.48781 14.7796 6.61755 14.6655 6.7317L14.6637 6.73348L14.6329 6.76426C14.2107 3.35588 12.6083 1.7368 11.1654 1.00465C11.148 0.987812 11.1265 0.967972 11.1036 0.950782C11.0775 0.931205 11.0311 0.900467 10.9694 0.888912C10.2276 0.608301 9.41043 1.01168 9.1237 1.77629L9.12314 1.7778C8.50566 3.46558 7.35287 4.62281 6.16627 5.76704C4.51756 7.33121 2.75938 9.03623 2.80163 12.093C2.75906 14.055 3.54464 15.8826 4.89054 17.272ZM3.04999 13.0648C3.01092 12.7459 2.99439 12.4218 3.00168 12.0939C2.95933 9.12977 4.6531 7.47834 6.30453 5.9116C7.49017 4.76831 8.67581 3.58267 9.31098 1.84655C9.56504 1.16904 10.2849 0.830288 10.9201 1.08435C10.9624 1.08435 11.0048 1.1267 11.0471 1.16904C11.9267 1.60884 12.8853 2.39635 13.5753 3.77299C12.8853 2.39633 11.9267 1.6088 11.0471 1.16901C11.0047 1.12666 10.9624 1.08432 10.9201 1.08432C10.2849 0.830251 9.56503 1.16901 9.31097 1.84651C8.6758 3.58263 7.49016 4.76827 6.30452 5.91157C4.65309 7.47831 2.95932 9.12973 3.00166 12.0938C2.99438 12.4218 3.01092 12.7459 3.04999 13.0648ZM14.477 7.18189C14.477 7.1819 14.477 7.18192 14.477 7.18193C14.5828 7.09724 14.6887 6.99138 14.8052 6.87493C14.9216 6.75849 15.0486 6.63146 15.1968 6.50442C15.4932 6.20801 15.9167 6.08098 16.2978 6.16567C16.7212 6.25036 17.06 6.50443 17.2294 6.84318C17.3088 7.0046 17.3835 7.16371 17.4539 7.32035C17.3835 7.1637 17.3087 7.00458 17.2294 6.84314C17.06 6.50439 16.7212 6.25032 16.2978 6.16563C15.9167 6.08094 15.4932 6.20798 15.1968 6.50439C15.0486 6.63142 14.9216 6.75845 14.8051 6.87489L14.8051 6.8749C14.7557 6.92437 14.7081 6.97191 14.6617 7.01675C14.5988 7.07745 14.5379 7.13318 14.477 7.18189ZM12.6977 6.35925C12.8428 7.15476 12.8833 7.97963 12.8679 8.74864C12.8679 8.79032 12.8704 8.8315 12.8754 8.87195C12.8704 8.83151 12.8679 8.79035 12.8679 8.74867C12.8833 7.97966 12.8428 7.15478 12.6977 6.35925ZM16.7358 10.6654C16.8108 11.1478 16.8483 11.6393 16.8483 12.1362V12.1786C16.8483 13.703 16.2978 15.2274 15.2392 16.3283C14.0535 17.4293 12.5291 18.0221 10.9201 17.9374C9.1416 17.9374 7.44783 17.2175 6.21984 15.9472C5.82743 15.5548 5.50448 15.1119 5.25584 14.6331C5.50447 15.1119 5.82743 15.5548 6.21983 15.9472C7.44782 17.2175 9.14159 17.9373 10.9201 17.9373C12.5291 18.022 14.0535 17.4292 15.2392 16.3283C16.2978 15.2273 16.8483 13.7029 16.8483 12.1785V12.1362C16.8483 11.6393 16.8108 11.1478 16.7358 10.6654ZM10.9306 17.7376C12.4802 17.8192 13.9509 17.2497 15.0989 16.1856C16.1154 15.1261 16.6483 13.655 16.6483 12.1785V12.1362C16.6483 10.8624 16.3969 9.6266 15.8955 8.49474C15.2436 9.11663 14.7845 9.49093 14.4179 9.68717C14.2122 9.79725 14.0268 9.85633 13.846 9.86789C13.6644 9.8795 13.5028 9.84219 13.3473 9.78249C12.9207 9.62211 12.6679 9.20129 1Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture></a></div></div></div><div style="" class="vui_carousel__slide vui_carousel__slide--next"><div class="carousel-area" data-index="0" data-v-ba17e0dd=""><div class="carousel-area-img" data-v-ba17e0dd=""><div data-v-ba17e0dd="" class="extension-tips-v2"></div><a data-loc-id="4694" class="carousel-item" data-v-ba17e0dd="" href="https://www.bilibili.com/video/BV1Q1j36FEfj" rel="noopener" target="_blank" data-target-url="https://www.bilibili.com/video/BV1Q1j36FEfj"><!----><!--[--><picture class="v-img carousel-inner__img" data-v-ba17e0dd="" data-v-339aeb28=""><!--[--><!--[--><source srcset="//i0.hdslb.com/bfs/banner/e2d2c18d68725eb5c256bbb69770222f0115d264.png@976w_550h_!web-home-carousel-cover.avif?mirror_report_swipe=1" type="image/avif" data-v-339aeb28=""><!--]--><!--[--><source srcset="//i0.hdslb.com/bfs/banner/e2d2c18d68725eb5c256bbb69770222f0115d264.png@976w_550h_!web-home-carousel-cover.webp?mirror_report_swipe=1" type="image/webp" data-v-339aeb28=""><!--]--><!--[--><img src="//i0.hdslb.com/bfs/banner/e2d2c18d68725eb5c256bbb69770222f0115d264.png@976w_550h_!web-home-carousel-cover?mirror_report_swipe=1" alt="来听伦敦大师赛胜者组应援金曲!" loading="eager" onload="fsrCb();firstSwipeLoaded(0)" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><!--]--></a></div></div></div><div style="" class="vui_carousel__slide"><div class="carousel-area" data-index="1" data-v-ba17e0dd=""><div class="carousel-area-img" data-v-ba17e0dd=""><div data-v-ba17e0dd="" class="extension-tips-v2"></div><a data-loc-id="4694" class="carousel-item" data-v-ba17e0dd="" href="//cm.bilibili.com/cm/api/fees/pc/sync/v2?msg=a%7C4696%2Cb%7Cbilibili%2Cc%7C0%2Cd%7C1%2Ce%7CCAAQABiAoJC7xrnynRAgACgAMCQ42CRCIDE3ODE2NzU2Njk4OTVxMTBhODRhMTY1YTE5N3EzMzcxSIfzlKHtM1IG6Z2S5bKbWgblsbHkuJxiBuS4reWbvWhkcAB4gICAgMAhgAEAiAHImAWSAQ4yMjIuMjA2LjE4LjE0NpoBAKABALIBICgHA8Grk3nfQUBPzs45MkRTum%2FQ%2ByXwonjofYYD6PJTugGkBmh0dHBzOi8vZ2FtZS1kYXRhLWFwaS5iaWxpZ2FtZS5jb20vdjIvYXBpL2NsaWNrL3BjX2JpbGk%2FYWNjb3VudF9pZD0zNiZjYW1wYWlnbl9pZD1fX0NBTVBBSUdOSURfXyZ1bml0X2lkPV9fVU5JVElEX18mY3JlYXRpdmVfaWQ9MTE2OTc1MDQwNzk3Nzg5Nzk4NCZzdXBwbHlfY2FsbGJhY2s9cGJhZXMuRVZubk5maGIxQkJNY1djb0pKdDZGRnM3Wk9qY2VtY1p0Y1N3enlrZTZkY190eVZCWllRUWVrZlQ3VUxaR0F5QnJSSEhDVkNPZ3ZQTTBCOUlLMGtYSmclM0QlM0QmY2FsbGJhY2tfdXJsPWh0dHBzJTNhJTJmJTJmY20uYmlsaWJpbGkuY29tJTJmY29udiUyZmFwaSUyZmNvbnZlcnNpb24lMmZhZCUyZmNiJTJmdjElM2Z0cmFja19pZCUzZHBiYWVzLkVWbm5OZmhiMUJCTWNXY29KSnQ2RkZzN1pPamNlbWNadGNTd3p5a2U2ZGNfdHlWQlpZUVFla2ZUN1VMWkdBeUJyUkhIQ1ZDT2d2UE0wQjlJSzBrWEpnJTNEJTNEJm1pZD0wJmlwPTIyMi4yMDYuMTguMTQ2JnJlcXVlc3RfaWQ9MTc4MTY3NTY2OTg5NXExMGE4NGExNjVhMTk3cTMzNzEmdHM9X19UU19fJnVhPU1vemlsbGElMkY1LjArJTI4V2luZG93cytOVCsxMC4wJTNCK1dpbjY0JTNCK3g2NCUyOStBcHBsZVdlYktpdCUyRjUzNy4zNislMjhLSFRNTCUyQytsaWtlK0dlY2tvJTI5K0Nocm9tZSUyRjE0OS4wLjAuMCtTYWZhcmklMkY1MzcuMzYrRWRnJTJGMTQ5LjAuMC4wJnNob3J0X3RyYWNraWQ9Y2FjM2JiMTNlMjE3NWYxYzRhYTlhNWVhM2NlY2Y4NjUmYWRfY2hhbm5lbF9pZD0zJmFkX2Y9ZGViNzRhYzU0Njc3NGNjMzhlMWFiMzMxMThkYmU2ZTMmZ2FtZV9pZD0xMTYyMsIBANIBANgBjALgAQDoAQDwAQCAAgKIAgC4AgDAAgDQAgDYAgDqAgDwAsrAK%2FgCAIgDAJIDAKgDALADALgDAMIDAMgDn40G0gObAXsiMSI6IjExNjk3NTA0MDc5Nzc4OTc5ODQiLCIyIjoiMzYiLCIzIjoiMzYiLCI2IjoiMzZfMCIsIjEyIjoiNDY5NiIsIjEzIjoiNyIsIjE0IjoiMCIsIjE1IjoiMCIsIjE2IjoiN18wIiwiMTciOiI0Njk0IiwiMTkiOiI4NTA2NCIsIjI1IjoiMTI5MCIsIjI2IjoiMTI5MSJ94AMA6AMA8AMA%2BgMFb3RoZXKCBAludWxsOm51bGyYBACgBACqBAQIABAEsAQA4gSMAjU2LnsicHNJZCI6MTM0MDQ2LCJ2MiI6IkFQQXF2Yk10TjJic3B6R0hXV3UwYmJhMVJ1emt6OF82YUtPTmgzLU1qTW9IRWJUUnJMd0QyN2JxTEVrYldoYzVsWUxrTjRIZjc3Yk1DcUpvWU05RkRLVlljR1Z0NlQtVHJzMTdiME9mM3dvb2RoUm5TYXlHWUhfaE91VEJTckV2N1JsLUZiUGpuQkN6eHRJVTY4WFdMai1wZTFWX0JmWXNfSXpoRWthWE5mZUluT2FHVVcwaVJBZyJ9OzYzLnsicHNJZCI6MTMzMDcxLCJ2MiI6IktPQSJ9OzcwLnsicHNJZCI6MTE0NzgwLCJ2MiI6IkFBIn36BAJ7fZAFgASQBYAGkAWBBpAFgQiQBYIGkAUEkAWFApAFjgKQBY8GkAWYApAFGZAFmgKQBZ0CkAWfApAFJpAFpgaQBasEkAWvBJAFsASQBbEEkAWyBJAFM5AFswKQBbQEkAW3ApAFtwiQBbgEkAW6BpAFO5AFQ5AFwwKQBUiQBcgIkAXMApAFzgKQBVOQBdMCkAXWApAF1wKQBdgCkAXaApAF3AKQBd8CkAVikAXlBJAF7QSQBfACkAX7ApAF%2FwKQBYQBkAWGAZAFhgOQBYgBkAWJAZAFjQOQBZIDkAWTB5AFlAOQBZgDkAWYEZAFmwOQBZwDkAWhB5AFogOQBaQDkAWnA5AFrAGQBbADkAWxAZAFsQOQBbMBkAW0AZAFwAOQBcEDkAXGBZAFyAGQBc0DkAXQAZAF0AOQBdMDkAXXA5AF2AGQBd8FkAXlA5AF6AGQBegDkAXqA5AF6wOQBfEBkAXyA5AF9QOQBfcDkAX4A5AF%2BgOQBfsDkAX%2FA6AFAMgFBNIFAOAFAw%3D%3D%2Cf%7Cclick_sync_3%2Cg%7C1%2Ch%7C2%2Ci%7C%2Cj%7C%2Ck%7C1781675673427%2Cl%7C4694%2Cm%7C1781675673345%2Cn%7C1%2Co%7C0%2Cp%7Cad_card&ts=1781675673427" rel="noopener" target="_blank" data-target-url="https://game-data-api.biligame.com/v2/api/click/pc_bili?account_id=36&campaign_id=__CAMPAIGNID__&unit_id=__UNITID__&creative_id=1169750407977897984&supply_callback=pbaes.EVnnNfhb1BBMcWcoJJt6FFs7ZOjcemcZtcSwzyke6dc_tyVBZYQQekfT7ULZGAyBrRHHCVCOgvPM0B9IK0kXJg%3D%3D&callback_url=https%3A%2F%2Fcm.bilibili.com%2Fconv%2Fapi%2Fconversion%2Fad%2Fcb%2Fv1%3Ftrack_id%3Dpbaes.EVnnNfhb1BBMcWcoJJt6FFs7ZOjcemcZtcSwzyke6dc_tyVBZYQQekfT7ULZGAyBrRHHCVCOgvPM0B9IK0kXJg%3D%3D&mid=0&ip=222.206.18.146&request_id=1781675669895q10a84a165a197q3371&ts=__TS__&ua=Mozilla%2F5.0%2B(Windows%2BNT%2B10.0%3B%2BWin64%3B%2Bx64)%2BAppleWebKit%2F537.36%2B(KHTML%2C%2Blike%2BGecko)%2BChrome%2F149.0.0.0%2BSafari%2F537.36%2BEdg%2F149.0.0.0&short_trackid=cac3bb13e2175f1c4aa9a5ea3cecf865&ad_channel_id=3&ad_f=deb74ac546774cc38e1ab33118dbe6e3&game_id=11622"><!----><picture data-v-339aeb28="" data-v-ba17e0dd="" class="v-img carousel-inner__img"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/sycp_brand/creative_img/202606/bb8950e9f32288c590c07272b6d4526d.png@976w_550h_!web-home-carousel-cover.avif" type="image/avif"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/sycp_brand/creative_img/202606/bb8950e9f32288c590c07272b6d4526d.png@976w_550h_!web-home-carousel-cover.webp" type="image/webp"><img data-v-339aeb28="" src="//i1.hdslb.com/bfs/sycp_brand/creative_img/202606/bb8950e9f32288c590c07272b6d4526d.png@976w_550h_!web-home-carousel-cover" alt="百将牌上线炸弹房玩法!" loading="eager" onload="fsrCb();firstSwipeLoaded(1)" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture></a></div></div></div><div style="" class="vui_carousel__slide"><div class="carousel-area" data-index="2" data-v-ba17e0dd=""><div class="carousel-area-img" data-v-ba17e0dd=""><div data-v-ba17e0dd="" class="extension-tips-v2"></div><a data-loc-id="4694" class="carousel-item" data-v-ba17e0dd="" href="https://www.bilibili.com/bangumi/play/ep4011535" rel="noopener" target="_blank" data-target-url="https://www.bilibili.com/bangumi/play/ep4011535"><!----><picture data-v-339aeb28="" data-v-ba17e0dd="" class="v-img carousel-inner__img"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/e9ee88cc6b3b01834004f5615650781cf7b37237.png@976w_550h_!web-home-carousel-cover.avif" type="image/avif"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/e9ee88cc6b3b01834004f5615650781cf7b37237.png@976w_550h_!web-home-carousel-cover.webp" type="image/webp"><img data-v-339aeb28="" src="//i1.hdslb.com/bfs/banner/e9ee88cc6b3b01834004f5615650781cf7b37237.png@976w_550h_!web-home-carousel-cover" alt="入围釜山电影节,海边唱响坚韧茂腔" loading="eager" onload="fsrCb();firstSwipeLoaded(2)" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture></a></div></div></div><div style="" class="vui_carousel__slide"><div class="carousel-area" data-index="3" data-v-ba17e0dd=""><div class="carousel-area-img" data-v-ba17e0dd=""><div data-v-ba17e0dd="" class="extension-tips-v2"></div><a data-loc-id="4694" class="carousel-item" data-v-ba17e0dd="" href="https://www.bilibili.com/bangumi/play/ep4240251" rel="noopener" target="_blank" data-target-url="https://www.bilibili.com/bangumi/play/ep4240251"><!----><picture data-v-339aeb28="" data-v-ba17e0dd="" class="v-img carousel-inner__img"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/f7e78feb43816aba709e0776b5d237904c7994b3.jpg@976w_550h_!web-home-carousel-cover.avif" type="image/avif"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/f7e78feb43816aba709e0776b5d237904c7994b3.jpg@976w_550h_!web-home-carousel-cover.webp" type="image/webp"><img data-v-339aeb28="" src="//i1.hdslb.com/bfs/banner/f7e78feb43816aba709e0776b5d237904c7994b3.jpg@976w_550h_!web-home-carousel-cover" alt="生日回顾上线!贺峻霖June with Jun" loading="eager" onload="fsrCb();firstSwipeLoaded(3)" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture></a></div></div></div><div style="" class="vui_carousel__slide"><div class="carousel-area" data-index="4" data-v-ba17e0dd=""><div class="carousel-area-img" data-v-ba17e0dd=""><div data-v-ba17e0dd="" class="extension-tips-v2"></div><a data-loc-id="4694" class="carousel-item" data-v-ba17e0dd="" href="https://www.bilibili.com/bangumi/play/ep3537933" rel="noopener" target="_blank" data-target-url="https://www.bilibili.com/bangumi/play/ep3537933"><!----><picture data-v-339aeb28="" data-v-ba17e0dd="" class="v-img carousel-inner__img"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/e111a9c16c92a87935167502f786ddc7e34c8b73.png@976w_550h_!web-home-carousel-cover.avif" type="image/avif"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/e111a9c16c92a87935167502f786ddc7e34c8b73.png@976w_550h_!web-home-carousel-cover.webp" type="image/webp"><img data-v-339aeb28="" src="//i1.hdslb.com/bfs/banner/e111a9c16c92a87935167502f786ddc7e34c8b73.png@976w_550h_!web-home-carousel-cover" alt="你有没有想过到外面去看看?" loading="eager" onload="fsrCb();firstSwipeLoaded(4)" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture></a></div></div></div><div style="" class="vui_carousel__slide"><div class="carousel-area" data-index="5" data-v-ba17e0dd=""><div class="carousel-area-img" data-v-ba17e0dd=""><div data-v-ba17e0dd="" class="extension-tips-v2"></div><a data-loc-id="4694" class="carousel-item" data-v-ba17e0dd="" href="https://www.bilibili.com/video/BV1kcjg6gELc" rel="noopener" target="_blank" data-target-url="https://www.bilibili.com/video/BV1kcjg6gELc"><!----><picture data-v-339aeb28="" data-v-ba17e0dd="" class="v-img carousel-inner__img"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/39ce4f07432f93e1db63eac4b07ee59472eda2c6.jpg@976w_550h_!web-home-carousel-cover.avif" type="image/avif"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/39ce4f07432f93e1db63eac4b07ee59472eda2c6.jpg@976w_550h_!web-home-carousel-cover.webp" type="image/webp"><img data-v-339aeb28="" src="//i1.hdslb.com/bfs/banner/39ce4f07432f93e1db63eac4b07ee59472eda2c6.jpg@976w_550h_!web-home-carousel-cover" alt="梅西10大一条龙" loading="eager" onload="fsrCb();firstSwipeLoaded(5)" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture></a></div></div></div><div style="" class="vui_carousel__slide"><div class="carousel-area" data-index="6" data-v-ba17e0dd=""><div class="carousel-area-img" data-v-ba17e0dd=""><div data-v-ba17e0dd="" class="extension-tips-v2"></div><a data-loc-id="4694" class="carousel-item" data-v-ba17e0dd="" href="https://www.bilibili.com/bangumi/play/ep336480" rel="noopener" target="_blank" data-target-url="https://www.bilibili.com/bangumi/play/ep336480"><!----><picture data-v-339aeb28="" data-v-ba17e0dd="" class="v-img carousel-inner__img"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/131cb3ac05f437b67411a43457346da45941d43b.png@976w_550h_!web-home-carousel-cover.avif" type="image/avif"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/131cb3ac05f437b67411a43457346da45941d43b.png@976w_550h_!web-home-carousel-cover.webp" type="image/webp"><img data-v-339aeb28="" src="//i1.hdslb.com/bfs/banner/131cb3ac05f437b67411a43457346da45941d43b.png@976w_550h_!web-home-carousel-cover" alt="梅西:从无名少年到绿茵传奇" loading="eager" onload="fsrCb();firstSwipeLoaded(6)" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture></a></div></div></div><div style="" class="vui_carousel__slide"><div class="carousel-area" data-index="7" data-v-ba17e0dd=""><div class="carousel-area-img" data-v-ba17e0dd=""><div data-v-ba17e0dd="" class="extension-tips-v2"></div><a data-loc-id="4694" class="carousel-item" data-v-ba17e0dd="" href="//www.bilibili.com/video/BV1UFJN6jEZz?track_id=" rel="noopener" target="_blank" data-target-url="http://www.bilibili.com/video/BV1UFJN6jEZz?track_id="><!----><picture data-v-339aeb28="" data-v-ba17e0dd="" class="v-img carousel-inner__img"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/archive/8fdde637aaee057b244a01cd6931a1b0c253714f.jpg@976w_550h_!web-home-carousel-cover.avif" type="image/avif"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/archive/8fdde637aaee057b244a01cd6931a1b0c253714f.jpg@976w_550h_!web-home-carousel-cover.webp" type="image/webp"><img data-v-339aeb28="" src="//i1.hdslb.com/bfs/archive/8fdde637aaee057b244a01cd6931a1b0c253714f.jpg@976w_550h_!web-home-carousel-cover" alt="硬核视频播客:深度剖析近三个月国际局势" loading="eager" onload="fsrCb();firstSwipeLoaded(7)" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture></a></div></div></div><div style="" class="vui_carousel__slide"><div class="carousel-area" data-index="8" data-v-ba17e0dd=""><div class="carousel-area-img" data-v-ba17e0dd=""><div data-v-ba17e0dd="" class="extension-tips-v2"></div><a data-loc-id="4694" class="carousel-item" data-v-ba17e0dd="" href="https://www.bilibili.com/bangumi/play/ep3409886" rel="noopener" target="_blank" data-target-url="https://www.bilibili.com/bangumi/play/ep3409886"><!----><picture data-v-339aeb28="" data-v-ba17e0dd="" class="v-img carousel-inner__img"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/13804c38473d979b06f6fbc5ccc07b96efab2641.png@976w_550h_!web-home-carousel-cover.avif" type="image/avif"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/banner/13804c38473d979b06f6fbc5ccc07b96efab2641.png@976w_550h_!web-home-carousel-cover.webp" type="image/webp"><img data-v-339aeb28="" src="//i1.hdslb.com/bfs/banner/13804c38473d979b06f6fbc5ccc07b96efab2641.png@976w_550h_!web-home-carousel-cover" alt="今夜过后,长桉的传说将再添一笔" loading="eager" onload="fsrCb();firstSwipeLoaded(8)" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture></a></div></div></div><div style="" class="vui_carousel__slide vui_carousel__slide--prev"><div class="carousel-area" data-index="0" data-v-ba17e0dd=""><div class="carousel-area-img" data-v-ba17e0dd=""><div data-v-ba17e0dd="" class="extension-tips-v2"></div><a data-loc-id="4694" class="carousel-item" data-v-ba17e0dd="" href="https://www.bilibili.com/video/BV1Q1j36FEfj" rel="noopener" target="_blank" data-target-url="https://www.bilibili.com/video/BV1Q1j36FEfj"><!----><!--[--><picture class="v-img carousel-inner__img" data-v-ba17e0dd="" data-v-339aeb28=""><!--[--><!--[--><source srcset="//i0.hdslb.com/bfs/banner/e2d2c18d68725eb5c256bbb69770222f0115d264.png@976w_550h_!web-home-carousel-cover.avif?mirror_report_swipe=1" type="image/avif" data-v-339aeb28=""><!--]--><!--[--><source srcset="//i0.hdslb.com/bfs/banner/e2d2c18d68725eb5c256bbb69770222f0115d264.png@976w_550h_!web-home-carousel-cover.webp?mirror_report_swipe=1" type="image/webp" data-v-339aeb28=""><!--]--><!--[--><img src="//i0.hdslb.com/bfs/banner/e2d2c18d68725eb5c256bbb69770222f0115d264.png@976w_550h_!web-home-carousel-cover?mirror_report_swipe=1" alt="来听伦敦大师赛胜者组应援金曲!" loading="eager" onload="fsrCb();firstSwipeLoaded(0)" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><!--]--></a></div></div></div><!--]--><div class="vui_carousel-transform clearfix"></div></div><!--[--><div class="carousel-footer" data-v-ba17e0dd=""><div class="carousel-footer-mask" style="background-color: rgb(70, 54, 65);" data-v-ba17e0dd=""></div><div class="carousel-footer-text" data-v-ba17e0dd=""><div class="carousel-footer-title" data-v-ba17e0dd=""><!----><a href="https://www.bilibili.com/video/BV1Q1j36FEfj" target="_blank" data-v-ba17e0dd=""><span data-v-ba17e0dd="">来听伦敦大师赛胜者组应援金曲!</span></a></div><!----></div><div class="carousel-dots" data-v-ba17e0dd=""><ul class="carousel-dots-list" data-v-ba17e0dd=""><!--[--><li class="carousel-dots-dot is-active" data-v-ba17e0dd=""><div class="before" data-v-ba17e0dd=""></div><div class="after" data-v-ba17e0dd=""></div></li><li class="carousel-dots-dot" data-v-ba17e0dd=""><div class="before" data-v-ba17e0dd=""></div><div class="after" data-v-ba17e0dd=""></div></li><li class="carousel-dots-dot" data-v-ba17e0dd=""><div class="before" data-v-ba17e0dd=""></div><div class="after" data-v-ba17e0dd=""></div></li><li class="carousel-dots-dot" data-v-ba17e0dd=""><div class="before" data-v-ba17e0dd=""></div><div class="after" data-v-ba17e0dd=""></div></li><li class="carousel-dots-dot" data-v-ba17e0dd=""><div class="before" data-v-ba17e0dd=""></div><div class="after" data-v-ba17e0dd=""></div></li><li class="carousel-dots-dot" data-v-ba17e0dd=""><div class="before" data-v-ba17e0dd=""></div><div class="after" data-v-ba17e0dd=""></div></li><li class="carousel-dots-dot" data-v-ba17e0dd=""><div class="before" data-v-ba17e0dd=""></div><div class="after" data-v-ba17e0dd=""></div></li><li class="carousel-dots-dot" data-v-ba17e0dd=""><div class="before" data-v-ba17e0dd=""></div><div class="after" data-v-ba17e0dd=""></div></li><li class="carousel-dots-dot" data-v-ba17e0dd=""><div class="before" data-v-ba17e0dd=""></div><div class="after" data-v-ba17e0dd=""></div></li><!--]--></ul></div></div><!--]--><!--[--><div class="carousel-arrows" data-v-ba17e0dd=""><button data-v-ba17e0dd=""><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" data-v-ba17e0dd=""><!--[--><path d="M15.4571 3.04289C15.8476 3.43342 15.8476 4.06658 15.4571 4.45711L8.09098 11.8232C7.99335 11.9209 7.99335 12.0791 8.09098 12.1768L15.4571 19.5429C15.8476 19.9334 15.8476 20.5666 15.4571 20.9571C15.0666 21.3476 14.4334 21.3476 14.0429 20.9571L6.67677 13.591C5.79809 12.7123 5.79809 11.2877 6.67677 10.409L14.0429 3.04289C14.4334 2.65237 15.0666 2.65237 15.4571 3.04289z" fill="currentColor"></path><!--]--></svg></button><button data-v-ba17e0dd=""><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" data-v-ba17e0dd=""><!--[--><path d="M8.5429 3.04289C8.15237 3.43342 8.15237 4.06658 8.5429 4.45711L15.909 11.8232C16.0066 11.9209 16.0066 12.0791 15.909 12.1768L8.5429 19.5429C8.15237 19.9334 8.15237 20.5666 8.5429 20.9571C8.93342 21.3476 9.56659 21.3476 9.95711 20.9571L17.3232 13.591C18.2019 12.7123 18.2019 11.2877 17.3232 10.409L9.95711 3.04289C9.56659 2.65237 8.93342 2.65237 8.5429 3.04289z" fill="currentColor"></path><!--]--></svg></button></div><!--]--></div></div></div></div></div></div></div><!--[--><!--[--><div class="feed-card" data-v-8b105adf=""><div class="bili-feed-card" data-v-8b105adf=""><div class="bili-video-card is-rcmd" style="--cover-radio:56.25%;"><!----><!----><div class="bili-video-card__wrap"><div class="bili-video-card__no-interest" style="display: none;"><div class="bili-video-card__no-interest--inner"><div class="bili-video-card__no-interest--left"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 36 36" width="36" height="36" class="no-interest-icon" fill="currentColor"><path d="M3 18C3 9.715724999999999 9.715724999999999 3 18 3C26.284274999999997 3 33 9.715724999999999 33 18C33 26.284274999999997 26.284274999999997 33 18 33C9.715724999999999 33 3 26.284274999999997 3 18zM12.796710000000001 12.694004999999999C12.358049999999999 12.253995 11.645745 12.2529 11.205735 12.691575C10.765709999999999 13.130234999999999 10.764615 13.842555 11.20329 14.282565000000002L13.41144 16.494975L11.20329 18.7074C10.764615 19.147350000000003 10.765709999999999 19.8597 11.205735 20.298375C11.645745 20.73705 12.358049999999999 20.735925 12.796710000000001 20.295975L15.2682 17.81895C15.99795 17.087175000000002 15.99795 15.902775000000002 15.2682 15.17097L12.796710000000001 12.694004999999999zM24.794325 12.691575C24.354300000000002 12.2529 23.64195 12.253995 23.203274999999998 12.694004999999999L20.7318 15.17097C20.00205 15.902775000000002 20.00205 17.087175000000002 20.7318 17.81895L23.203274999999998 20.295975C23.64195 20.735925 24.354300000000002 20.73705 24.794325 20.298375C25.234274999999997 19.8597 25.2354 19.147350000000003 24.796725000000002 1Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><div class="v-inline-player"></div></div><div class="bili-video-card__mask"><div class="bili-video-card__stats"><div class="bili-video-card__stats--left"><!--[--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M14.7138 10.96875C15.50765 11.4271 15.50765 12.573 14.71375 13.0313L11.5362 14.8659C10.74235 15.3242 9.75 14.7513 9.75001 13.8346L9.75001 10.1655C9.75001 9.24881 10.74235 8.67587 11.5362 9.13422L14.7138 10.96875z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">260.9万</span></span><!--]--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M15.875 10.75L9.875 10.75C9.46079 10.75 9.125 10.4142 9.125 10C9.125 9.58579 9.46079 9.25 9.875 9.25L15.875 9.25C16.2892 9.25 16.625 9.58579 16.625 10C16.625 10.4142 16.2892 10.75 15.875 10.75z" fill="currentColor"></path><path d="M17.375 14.75L11.375 14.75C10.9608 14.75 10.625 14.4142 10.625 14C10.625 13.5858 10.9608 13.25 11.375 13.25L17.375 13.25C17.7892 13.25 18.125 13.5858 18.125 14C18.125 14.4142 17.7892 14.75 17.375 14.75z" fill="currentColor"></path><path d="M7.875 10C7.875 10.4142 7.53921 10.75 7.125 10.75L6.625 10.75C6.21079 10.75 5.875 10.4142 5.875 10C5.875 9.58579 6.21079 9.25 6.625 9.25L7.125 9.25C7.53921 9.25 7.875 9.58579 7.875 10z" fill="currentColor"></path><path d="M9.375 14C9.375 14.4142 9.03921 14.75 8.625 14.75L8.125 14.75C7.71079 14.75 7.375 14.4142 7.375 14C7.375 13.5858 7.71079 13.25 8.125 13.25L8.625 13.25C9.03921 13.25 9.375 13.5858 9.375 14z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">322</span></span></div><span class="bili-video-card__stats__duration">01:52</span></div></div></div></a><div style="" class="bili-video-card__info"><div class="bili-video-card__info--right"><!--[--><div class="bili-video-card__info--no-interest" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="bili-video-card__info--no-interest--icon" fill="currentColor"><!--[--><path d="M13.62335 5.49835C13.62335 6.3949 12.8966 7.12171 12 7.12171C11.10345 7.12171 10.37665 6.3949 10.37665 5.49835C10.37665 4.6018 11.10345 3.875 12 3.875C12.8966 3.875 13.62335 4.6018 13.62335 5.49835zM13.62345 18.4985C13.62345 19.3951 12.8966 20.12195 12 20.12195C11.10335 20Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><div class="v-inline-player"></div></div><div class="bili-video-card__mask"><div class="bili-video-card__stats"><div class="bili-video-card__stats--left"><!--[--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M14.7138 10.96875C15.50765 11.4271 15.50765 12.573 14.71375 13.0313L11.5362 14.8659C10.74235 15.3242 9.75 14.7513 9.75001 13.8346L9.75001 10.1655C9.75001 9.24881 10.74235 8.67587 11.5362 9.13422L14.7138 10.96875z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">133.9万</span></span><!--]--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M15.875 10.75L9.875 10.75C9.46079 10.75 9.125 10.4142 9.125 10C9.125 9.58579 9.46079 9.25 9.875 9.25L15.875 9.25C16.2892 9.25 16.625 9.58579 16.625 10C16.625 10.4142 16.2892 10.75 15.875 10.75z" fill="currentColor"></path><path d="M17.375 14.75L11.375 14.75C10.9608 14.75 10.625 14.4142 10.625 14C10.625 13.5858 10.9608 13.25 11.375 13.25L17.375 13.25C17.7892 13.25 18.125 13.5858 18.125 14C18.125 14.4142 17.7892 14.75 17.375 14.75z" fill="currentColor"></path><path d="M7.875 10C7.875 10.4142 7.53921 10.75 7.125 10.75L6.625 10.75C6.21079 10.75 5.875 10.4142 5.875 10C5.875 9.58579 6.21079 9.25 6.625 9.25L7.125 9.25C7.53921 9.25 7.875 9.58579 7.875 10z" fill="currentColor"></path><path d="M9.375 14C9.375 14.4142 9.03921 14.75 8.625 14.75L8.125 14.75C7.71079 14.75 7.375 14.4142 7.375 14C7.375 13.5858 7.71079 13.25 8.125 13.25L8.625 13.25C9.03921 13.25 9.375 13.5858 9.375 14z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">2648</span></span></div><span class="bili-video-card__stats__duration">10:09</span></div></div></div></a><div style="" class="bili-video-card__info"><div class="bili-video-card__info--right"><!--[--><div class="bili-video-card__info--no-interest" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="bili-video-card__info--no-interest--icon" fill="currentColor"><!--[--><path d="M13.62335 5.49835C13.62335 6.3949 12.8966 7.12171 12 7.12171C11.10345 7.12171 10.37665 6.3949 10.37665 5.49835C10.37665 4.6018 11.10345 3.875 12 3.875C12.8966 3.875 13.62335 4.6018 13.62335 5.49835zM13.62345 18.4985C13.62345 19.3951 12.8966 20.12195 12 20.12195C11.10335 2Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><div class="v-inline-player"></div></div><div class="bili-video-card__mask"><div class="bili-video-card__stats"><div class="bili-video-card__stats--left"><!--[--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M14.7138 10.96875C15.50765 11.4271 15.50765 12.573 14.71375 13.0313L11.5362 14.8659C10.74235 15.3242 9.75 14.7513 9.75001 13.8346L9.75001 10.1655C9.75001 9.24881 10.74235 8.67587 11.5362 9.13422L14.7138 10.96875z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">305.5万</span></span><!--]--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M15.875 10.75L9.875 10.75C9.46079 10.75 9.125 10.4142 9.125 10C9.125 9.58579 9.46079 9.25 9.875 9.25L15.875 9.25C16.2892 9.25 16.625 9.58579 16.625 10C16.625 10.4142 16.2892 10.75 15.875 10.75z" fill="currentColor"></path><path d="M17.375 14.75L11.375 14.75C10.9608 14.75 10.625 14.4142 10.625 14C10.625 13.5858 10.9608 13.25 11.375 13.25L17.375 13.25C17.7892 13.25 18.125 13.5858 18.125 14C18.125 14.4142 17.7892 14.75 17.375 14.75z" fill="currentColor"></path><path d="M7.875 10C7.875 10.4142 7.53921 10.75 7.125 10.75L6.625 10.75C6.21079 10.75 5.875 10.4142 5.875 10C5.875 9.58579 6.21079 9.25 6.625 9.25L7.125 9.25C7.53921 9.25 7.875 9.58579 7.875 10z" fill="currentColor"></path><path d="M9.375 14C9.375 14.4142 9.03921 14.75 8.625 14.75L8.125 14.75C7.71079 14.75 7.375 14.4142 7.375 14C7.375 13.5858 7.71079 13.25 8.125 13.25L8.625 13.25C9.03921 13.25 9.375 13.5858 9.375 14z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">1295</span></span></div><span class="bili-video-card__stats__duration">02:59</span></div></div></div></a><div style="" class="bili-video-card__info"><div class="bili-video-card__info--right"><!--[--><div class="bili-video-card__info--no-interest" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="bili-video-card__info--no-interest--icon" fill="currentColor"><!--[--><path d="M13.62335 5.49835C13.62335 6.3949 12.8966 7.12171 12 7.12171C11.10345 7.12171 10.37665 6.3949 10.37665 5.49835C10.37665 4.6018 11.10345 3.875 12 3.875C12.8966 3.875 13.62335 4.6018 13.62335 5.49835zM13.62345 18.4985C13.62345 19.3951 12.8966 20.12195 12 20.12195C11.10335 2Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><div class="v-inline-player"></div></div><div class="bili-video-card__mask"><div class="bili-video-card__stats"><div class="bili-video-card__stats--left"><!--[--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M14.7138 10.96875C15.50765 11.4271 15.50765 12.573 14.71375 13.0313L11.5362 14.8659C10.74235 15.3242 9.75 14.7513 9.75001 13.8346L9.75001 10.1655C9.75001 9.24881 10.74235 8.67587 11.5362 9.13422L14.7138 10.96875z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">248.0万</span></span><!--]--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M15.875 10.75L9.875 10.75C9.46079 10.75 9.125 10.4142 9.125 10C9.125 9.58579 9.46079 9.25 9.875 9.25L15.875 9.25C16.2892 9.25 16.625 9.58579 16.625 10C16.625 10.4142 16.2892 10.75 15.875 10.75z" fill="currentColor"></path><path d="M17.375 14.75L11.375 14.75C10.9608 14.75 10.625 14.4142 10.625 14C10.625 13.5858 10.9608 13.25 11.375 13.25L17.375 13.25C17.7892 13.25 18.125 13.5858 18.125 14C18.125 14.4142 17.7892 14.75 17.375 14.75z" fill="currentColor"></path><path d="M7.875 10C7.875 10.4142 7.53921 10.75 7.125 10.75L6.625 10.75C6.21079 10.75 5.875 10.4142 5.875 10C5.875 9.58579 6.21079 9.25 6.625 9.25L7.125 9.25C7.53921 9.25 7.875 9.58579 7.875 10z" fill="currentColor"></path><path d="M9.375 14C9.375 14.4142 9.03921 14.75 8.625 14.75L8.125 14.75C7.71079 14.75 7.375 14.4142 7.375 14C7.375 13.5858 7.71079 13.25 8.125 13.25L8.625 13.25C9.03921 13.25 9.375 13.5858 9.375 14z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">7422</span></span></div><span class="bili-video-card__stats__duration">01:32</span></div></div></div></a><div style="" class="bili-video-card__info"><div class="bili-video-card__info--right"><!--[--><div class="bili-video-card__info--no-interest" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="bili-video-card__info--no-interest--icon" fill="currentColor"><!--[--><path d="M13.62335 5.49835C13.62335 6.3949 12.8966 7.12171 12 7.12171C11.10345 7.12171 10.37665 6.3949 10.37665 5.49835C10.37665 4.6018 11.10345 3.875 12 3.875C12.8966 3.875 13.62335 4.6018 13.62335 5.49835zM13.62345 18.4985C13.62345 19.3951 12.8966 20.12195 12 20.12195C11.10335 2Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><div class="v-inline-player"></div></div><div class="bili-video-card__mask"><div class="bili-video-card__stats"><div class="bili-video-card__stats--left"><!--[--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M14.7138 10.96875C15.50765 11.4271 15.50765 12.573 14.71375 13.0313L11.5362 14.8659C10.74235 15.3242 9.75 14.7513 9.75001 13.8346L9.75001 10.1655C9.75001 9.24881 10.74235 8.67587 11.5362 9.13422L14.7138 10.96875z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">95.0万</span></span><!--]--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M15.875 10.75L9.875 10.75C9.46079 10.75 9.125 10.4142 9.125 10C9.125 9.58579 9.46079 9.25 9.875 9.25L15.875 9.25C16.2892 9.25 16.625 9.58579 16.625 10C16.625 10.4142 16.2892 10.75 15.875 10.75z" fill="currentColor"></path><path d="M17.375 14.75L11.375 14.75C10.9608 14.75 10.625 14.4142 10.625 14C10.625 13.5858 10.9608 13.25 11.375 13.25L17.375 13.25C17.7892 13.25 18.125 13.5858 18.125 14C18.125 14.4142 17.7892 14.75 17.375 14.75z" fill="currentColor"></path><path d="M7.875 10C7.875 10.4142 7.53921 10.75 7.125 10.75L6.625 10.75C6.21079 10.75 5.875 10.4142 5.875 10C5.875 9.58579 6.21079 9.25 6.625 9.25L7.125 9.25C7.53921 9.25 7.875 9.58579 7.875 10z" fill="currentColor"></path><path d="M9.375 14C9.375 14.4142 9.03921 14.75 8.625 14.75L8.125 14.75C7.71079 14.75 7.375 14.4142 7.375 14C7.375 13.5858 7.71079 13.25 8.125 13.25L8.625 13.25C9.03921 13.25 9.375 13.5858 9.375 14z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">304</span></span></div><span class="bili-video-card__stats__duration">05:31</span></div></div></div></a><div style="" class="bili-video-card__info"><div class="bili-video-card__info--right"><!--[--><div class="bili-video-card__info--no-interest" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="bili-video-card__info--no-interest--icon" fill="currentColor"><!--[--><path d="M13.62335 5.49835C13.62335 6.3949 12.8966 7.12171 12 7.12171C11.10345 7.12171 10.37665 6.3949 10.37665 5.49835C10.37665 4.6018 11.10345 3.875 12 3.875C12.8966 3.875 13.62335 4.6018 13.62335 5.49835zM13.62345 18.4985C13.62345 19.3951 12.8966 20.12195 12 20.12195C11.10335 20.Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><div class="v-inline-player"></div></div><div class="bili-video-card__mask"><div class="bili-video-card__stats"><div class="bili-video-card__stats--left"><!--[--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M14.7138 10.96875C15.50765 11.4271 15.50765 12.573 14.71375 13.0313L11.5362 14.8659C10.74235 15.3242 9.75 14.7513 9.75001 13.8346L9.75001 10.1655C9.75001 9.24881 10.74235 8.67587 11.5362 9.13422L14.7138 10.96875z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">79.7万</span></span><!--]--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M15.875 10.75L9.875 10.75C9.46079 10.75 9.125 10.4142 9.125 10C9.125 9.58579 9.46079 9.25 9.875 9.25L15.875 9.25C16.2892 9.25 16.625 9.58579 16.625 10C16.625 10.4142 16.2892 10.75 15.875 10.75z" fill="currentColor"></path><path d="M17.375 14.75L11.375 14.75C10.9608 14.75 10.625 14.4142 10.625 14C10.625 13.5858 10.9608 13.25 11.375 13.25L17.375 13.25C17.7892 13.25 18.125 13.5858 18.125 14C18.125 14.4142 17.7892 14.75 17.375 14.75z" fill="currentColor"></path><path d="M7.875 10C7.875 10.4142 7.53921 10.75 7.125 10.75L6.625 10.75C6.21079 10.75 5.875 10.4142 5.875 10C5.875 9.58579 6.21079 9.25 6.625 9.25L7.125 9.25C7.53921 9.25 7.875 9.58579 7.875 10z" fill="currentColor"></path><path d="M9.375 14C9.375 14.4142 9.03921 14.75 8.625 14.75L8.125 14.75C7.71079 14.75 7.375 14.4142 7.375 14C7.375 13.5858 7.71079 13.25 8.125 13.25L8.625 13.25C9.03921 13.25 9.375 13.5858 9.375 14z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">374</span></span></div><span class="bili-video-card__stats__duration">01:59</span></div></div></div></a><div style="" class="bili-video-card__info"><div class="bili-video-card__info--right"><!--[--><div class="bili-video-card__info--no-interest" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="bili-video-card__info--no-interest--icon" fill="currentColor"><!--[--><path d="M13.62335 5.49835C13.62335 6.3949 12.8966 7.12171 12 7.12171C11.10345 7.12171 10.37665 6.3949 10.37665 5.49835C10.37665 4.6018 11.10345 3.875 12 3.875C12.8966 3.875 13.62335 4.6018 13.62335 5.49835zM13.62345 18.4985C13.62345 19.3951 12.8966 20.12195 12 20.12195C11.10335 20.Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><div class="v-inline-player"></div></div><div class="bili-video-card__mask"><div class="bili-video-card__stats"><div class="bili-video-card__stats--left"><!--[--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M14.7138 10.96875C15.50765 11.4271 15.50765 12.573 14.71375 13.0313L11.5362 14.8659C10.74235 15.3242 9.75 14.7513 9.75001 13.8346L9.75001 10.1655C9.75001 9.24881 10.74235 8.67587 11.5362 9.13422L14.7138 10.96875z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">150.1万</span></span><!--]--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M15.875 10.75L9.875 10.75C9.46079 10.75 9.125 10.4142 9.125 10C9.125 9.58579 9.46079 9.25 9.875 9.25L15.875 9.25C16.2892 9.25 16.625 9.58579 16.625 10C16.625 10.4142 16.2892 10.75 15.875 10.75z" fill="currentColor"></path><path d="M17.375 14.75L11.375 14.75C10.9608 14.75 10.625 14.4142 10.625 14C10.625 13.5858 10.9608 13.25 11.375 13.25L17.375 13.25C17.7892 13.25 18.125 13.5858 18.125 14C18.125 14.4142 17.7892 14.75 17.375 14.75z" fill="currentColor"></path><path d="M7.875 10C7.875 10.4142 7.53921 10.75 7.125 10.75L6.625 10.75C6.21079 10.75 5.875 10.4142 5.875 10C5.875 9.58579 6.21079 9.25 6.625 9.25L7.125 9.25C7.53921 9.25 7.875 9.58579 7.875 10z" fill="currentColor"></path><path d="M9.375 14C9.375 14.4142 9.03921 14.75 8.625 14.75L8.125 14.75C7.71079 14.75 7.375 14.4142 7.375 14C7.375 13.5858 7.71079 13.25 8.125 13.25L8.625 13.25C9.03921 13.25 9.375 13.5858 9.375 14z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">1.0万</span></span></div><span class="bili-video-card__stats__duration">11:03</span></div></div></div></a><div style="" class="bili-video-card__info"><div class="bili-video-card__info--right"><!--[--><div class="bili-video-card__info--no-interest" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="bili-video-card__info--no-interest--icon" fill="currentColor"><!--[--><path d="M13.62335 5.49835C13.62335 6.3949 12.8966 7.12171 12 7.12171C11.10345 7.12171 10.37665 6.3949 10.37665 5.49835C10.37665 4.6018 11.10345 3.875 12 3.875C12.8966 3.875 13.62335 4.6018 13.62335 5.49835zM13.62345 18.4985C13.62345 19.3951 12.8966 20.12195 12 20.12195C11.10335Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><div class="v-inline-player"></div></div><div class="bili-video-card__mask"><div class="bili-video-card__stats"><div class="bili-video-card__stats--left"><!--[--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M14.7138 10.96875C15.50765 11.4271 15.50765 12.573 14.71375 13.0313L11.5362 14.8659C10.74235 15.3242 9.75 14.7513 9.75001 13.8346L9.75001 10.1655C9.75001 9.24881 10.74235 8.67587 11.5362 9.13422L14.7138 10.96875z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">126.3万</span></span><!--]--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M15.875 10.75L9.875 10.75C9.46079 10.75 9.125 10.4142 9.125 10C9.125 9.58579 9.46079 9.25 9.875 9.25L15.875 9.25C16.2892 9.25 16.625 9.58579 16.625 10C16.625 10.4142 16.2892 10.75 15.875 10.75z" fill="currentColor"></path><path d="M17.375 14.75L11.375 14.75C10.9608 14.75 10.625 14.4142 10.625 14C10.625 13.5858 10.9608 13.25 11.375 13.25L17.375 13.25C17.7892 13.25 18.125 13.5858 18.125 14C18.125 14.4142 17.7892 14.75 17.375 14.75z" fill="currentColor"></path><path d="M7.875 10C7.875 10.4142 7.53921 10.75 7.125 10.75L6.625 10.75C6.21079 10.75 5.875 10.4142 5.875 10C5.875 9.58579 6.21079 9.25 6.625 9.25L7.125 9.25C7.53921 9.25 7.875 9.58579 7.875 10z" fill="currentColor"></path><path d="M9.375 14C9.375 14.4142 9.03921 14.75 8.625 14.75L8.125 14.75C7.71079 14.75 7.375 14.4142 7.375 14C7.375 13.5858 7.71079 13.25 8.125 13.25L8.625 13.25C9.03921 13.25 9.375 13.5858 9.375 14z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">4442</span></span></div><span class="bili-video-card__stats__duration">29:57</span></div></div></div></a><div style="" class="bili-video-card__info"><div class="bili-video-card__info--right"><!--[--><div class="bili-video-card__info--no-interest" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="bili-video-card__info--no-interest--icon" fill="currentColor"><!--[--><path d="M13.62335 5.49835C13.62335 6.3949 12.8966 7.12171 12 7.12171C11.10345 7.12171 10.37665 6.3949 10.37665 5.49835C10.37665 4.6018 11.10345 3.875 12 3.875C12.8966 3.875 13.62335 4.6018 13.62335 5.49835zM13.62345 18.4985C13.62345 19.3951 12.8966 20.12195 12 20.12195C11.10335 2Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><div class="v-inline-player"></div></div><div class="bili-video-card__mask"><div class="bili-video-card__stats"><div class="bili-video-card__stats--left"><!--[--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M14.7138 10.96875C15.50765 11.4271 15.50765 12.573 14.71375 13.0313L11.5362 14.8659C10.74235 15.3242 9.75 14.7513 9.75001 13.8346L9.75001 10.1655C9.75001 9.24881 10.74235 8.67587 11.5362 9.13422L14.7138 10.96875z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">6.7万</span></span><!--]--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M15.875 10.75L9.875 10.75C9.46079 10.75 9.125 10.4142 9.125 10C9.125 9.58579 9.46079 9.25 9.875 9.25L15.875 9.25C16.2892 9.25 16.625 9.58579 16.625 10C16.625 10.4142 16.2892 10.75 15.875 10.75z" fill="currentColor"></path><path d="M17.375 14.75L11.375 14.75C10.9608 14.75 10.625 14.4142 10.625 14C10.625 13.5858 10.9608 13.25 11.375 13.25L17.375 13.25C17.7892 13.25 18.125 13.5858 18.125 14C18.125 14.4142 17.7892 14.75 17.375 14.75z" fill="currentColor"></path><path d="M7.875 10C7.875 10.4142 7.53921 10.75 7.125 10.75L6.625 10.75C6.21079 10.75 5.875 10.4142 5.875 10C5.875 9.58579 6.21079 9.25 6.625 9.25L7.125 9.25C7.53921 9.25 7.875 9.58579 7.875 10z" fill="currentColor"></path><path d="M9.375 14C9.375 14.4142 9.03921 14.75 8.625 14.75L8.125 14.75C7.71079 14.75 7.375 14.4142 7.375 14C7.375 13.5858 7.71079 13.25 8.125 13.25L8.625 13.25C9.03921 13.25 9.375 13.5858 9.375 14z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">5556</span></span></div><span class="bili-video-card__stats__duration">37:58</span></div></div></div></a><div style="" class="bili-video-card__info"><div class="bili-video-card__info--right"><!--[--><div class="bili-video-card__info--no-interest" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="bili-video-card__info--no-interest--icon" fill="currentColor"><!--[--><path d="M13.62335 5.49835C13.62335 6.3949 12.8966 7.12171 12 7.12171C11.10345 7.12171 10.37665 6.3949 10.37665 5.49835C10.37665 4.6018 11.10345 3.875 12 3.875C12.8966 3.875 13.62335 4.6018 13.62335 5.49835zM13.62345 18.4985C13.62345 19.3951 12.8966 20.12195 12 20.12195C11.10335 20.Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
" data-v-339aeb28=""><!--]--><!--]--></picture><div class="v-inline-player"></div></div><div class="bili-video-card__mask"><div class="bili-video-card__stats"><div class="bili-video-card__stats--left"><!--[--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M14.7138 10.96875C15.50765 11.4271 15.50765 12.573 14.71375 13.0313L11.5362 14.8659C10.74235 15.3242 9.75 14.7513 9.75001 13.8346L9.75001 10.1655C9.75001 9.24881 10.74235 8.67587 11.5362 9.13422L14.7138 10.96875z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">50.8万</span></span><!--]--><span class="bili-video-card__stats--item"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="#ffffff" class="bili-video-card__stats--icon"><!--[--><path d="M12 4.99805C9.48178 4.99805 7.283 5.12616 5.73089 5.25202C4.65221 5.33949 3.81611 6.16352 3.72 7.23254C3.60607 8.4998 3.5 10.171 3.5 11.998C3.5 13.8251 3.60607 15.4963 3.72 16.76355C3.81611 17.83255 4.65221 18.6566 5.73089 18.7441C7.283 18.8699 9.48178 18.998 12 18.998C14.5185 18.998 16.7174 18.8699 18.2696 18.74405C19.3481 18.65655 20.184 17.8328 20.2801 16.76405C20.394 15.4973 20.5 13.82645 20.5 11.998C20.5 10.16965 20.394 8.49877 20.2801 7.23205C20.184 6.1633 19.3481 5.33952 18.2696 5.25205C16.7174 5.12618 14.5185 4.99805 12 4.99805zM5.60965 3.75693C7.19232 3.62859 9.43258 3.49805 12 3.49805C14.5677 3.49805 16.8081 3.62861 18.3908 3.75696C20.1881 3.90272 21.6118 5.29278 21.7741 7.09773C21.8909 8.3969 22 10.11405 22 11.998C22 13.88205 21.8909 15.5992 21.7741 16.8984C21.6118 18.7033 20.1881 20.09335 18.3908 20.23915C16.8081 20.3675 14.5677 20.498 12 20.498C9.43258 20.498 7.19232 20.3675 5.60965 20.2392C3.81206 20.0934 2.38831 18.70295 2.22603 16.8979C2.10918 15.5982 2 13.8808 2 11.998C2 10.1153 2.10918 8.39787 2.22603 7.09823C2.38831 5.29312 3.81206 3.90269 5.60965 3.75693z" fill="currentColor"></path><path d="M15.875 10.75L9.875 10.75C9.46079 10.75 9.125 10.4142 9.125 10C9.125 9.58579 9.46079 9.25 9.875 9.25L15.875 9.25C16.2892 9.25 16.625 9.58579 16.625 10C16.625 10.4142 16.2892 10.75 15.875 10.75z" fill="currentColor"></path><path d="M17.375 14.75L11.375 14.75C10.9608 14.75 10.625 14.4142 10.625 14C10.625 13.5858 10.9608 13.25 11.375 13.25L17.375 13.25C17.7892 13.25 18.125 13.5858 18.125 14C18.125 14.4142 17.7892 14.75 17.375 14.75z" fill="currentColor"></path><path d="M7.875 10C7.875 10.4142 7.53921 10.75 7.125 10.75L6.625 10.75C6.21079 10.75 5.875 10.4142 5.875 10C5.875 9.58579 6.21079 9.25 6.625 9.25L7.125 9.25C7.53921 9.25 7.875 9.58579 7.875 10z" fill="currentColor"></path><path d="M9.375 14C9.375 14.4142 9.03921 14.75 8.625 14.75L8.125 14.75C7.71079 14.75 7.375 14.4142 7.375 14C7.375 13.5858 7.71079 13.25 8.125 13.25L8.625 13.25C9.03921 13.25 9.375 13.5858 9.375 14z" fill="currentColor"></path><!--]--></svg><span class="bili-video-card__stats--text">162</span></span></div><span class="bili-video-card__stats__duration">03:11</span></div></div></div></a><div style="" class="bili-video-card__info"><div class="bili-video-card__info--right"><!--[--><div class="bili-video-card__info--no-interest" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="bili-video-card__info--no-interest--icon" fill="currentColor"><!--[--><path d="M13.62335 5.49835C13.62335 6.3949 12.8966 7.12171 12 7.12171C11.10345 7.12171 10.37665 6.3949 10.37665 5.49835C10.37665 4.6018 11.10345 3.875 12 3.875C12.8966 3.875 13.62335 4.6018 13.62335 5.49835zM13.62345 18.4985C13.62345 19.3951 12.8966 20.12195 12 20.12195C11.10335 20.Line truncated
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture><div data-v-315489fe="" class="detail-button">查看详情</div></div><!----><!----></div><div data-v-f5eb2b98="" class="watchlater-pip-button" data-watchlater-pip-entry="true"><div data-v-f5eb2b98="" class="watchlater-pip-button-inner-wrap"><div data-v-f5eb2b98="" class="watchlater-pip-button-inner expand-btn"><div data-v-f5eb2b98="" class="btn-icon"><svg data-v-f5eb2b98="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="btn-icon-inner svg-icon" fill="currentColor"><path d="M12 3.99976C7.58172 3.99976 4 7.58148 4 11.9998C4 16.418 7.58172 19.9998 12 19.9998C14.209 19.9998 16.2072 19.1058 17.656 17.6575C18.0465 17.2671 18.6797 17.2672 19.0702 17.6577C19.4606 18.0483 19.4605 18.6815 19.0699 19.072C17.2615 20.8798 14.7606 21.9998 12 21.9998C6.47715 21.9998 2 17.5226 2 11.9998C2 6.47691 6.47715 1.99976 12 1.99976C17.5228 1.99976 22 6.47691 22 11.9998C22 12.3745 21.9793 12.7449 21.939 13.1096C21.8783 13.6586 21.3841 14.0544 20.8352 13.9937C20.2863 13.933 19.8904 13.4388 19.9511 12.8899C19.9834 12.598 20 12.301 20 11.9998C20 7.58148 16.4183 3.99976 12 3.99976z" fill="currentColor"></path><path d="M18.2929 10.7926C18.6834 10.4021 19.3166 10.4021 19.7071 10.7926L21 12.0855L22.2929 10.7926C22.6834 10.4021 23.3166 10.4021 23.7071 10.7926C24.0976 11.1832 24.0976 11.8163 23.7071 12.2069L21.8839 14.0301C21.3957 14.5182 20.6043 14.5182 20.1161 14.0301L18.2929 12.2069C17.9024 11.8163 17.9024 11.1832 18.2929 10.7926z" fill="currentColor"></path><path d="M15.1659 11.0861C15.8694 11.4922 15.8694 12.5077 15.1659 12.9138L11.207 15.1995C10.5035 15.6056 9.62413 15.0979 9.62413 14.2856L9.62413 9.71432C9.62413 8.90197 10.5035 8.39425 11.207 8.80042L15.1659 11.0861z" fill="currentColor"></path></svg><picture data-v-339aeb28="" data-v-f5eb2b98="" class="v-img btn-icon-inner png-icon"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/static/jinkela/long/images/wlpip-playing-active.gif@48w_48h.avif" type="image/avif"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/static/jinkela/long/images/wlpip-playing-active.gif@48w_48h.webp" type="image/webp"><img data-v-339aeb28="" src="//i1.hdslb.com/bfs/static/jinkela/long/images/wlpip-playing-active.gif@48w_48h" alt="" loading="lazy" onload="" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture></div><div data-v-f5eb2b98="" class="btn-tips" style="display: none;">0</div></div><div data-v-f5eb2b98="" class="entry-toast" style=""><div data-v-f5eb2b98="" class="close"><svg data-v-f5eb2b98="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 12 12" width="12" height="12" fill="currentColor"><path d="M2.021815 2.0232C2.4123549999999985 1.632674999999999 3.0455000000000014 1.632674999999999 3.4360199999999987 2.0232L5.9992 4.586399999999999L8.56315 2.02238C8.953700000000003 1.631854999999999 9.586899999999996 1.631854999999999 9.977349999999996 2.02238C10.368050000000004 2.4129000000000014 10.368050000000004 3.0460649999999996 9.977349999999996 3.4365849999999987L7.4134 6.0006L9.976700000000005 8.564000000000004C10.367400000000004 8.954450000000003 10.367400000000004 9.587649999999996 9.976700000000005 9.978099999999996C9.586249999999996 10.36865 8.953049999999996 10.36865 8.5625 9.978099999999996L5.9992 7.4148000000000005L3.43669 9.9774C3.0461450000000014 10.36785 2.4130050000000005 10.36785 2.02246 9.9774C1.6319399999999988 9.5867 1.6319399999999988 8.953650000000003 2.02246 8.563200000000004L4.584999999999999 6.0006L2.021815 3.4374049999999987C1.6312900000000008 3.0468849999999996 1.6312900000000008 2.4137200000000014 2.021815 2.0232z" fill="currentColor"></path></svg></div><div data-v-f5eb2b98="" class="text">点击视频卡片右上角添加!</div></div></div></div><div data-v-4f27ff84="" class="flexible-roll-btn hidden"><div data-v-4f27ff84="" class="flexible-roll-btn-inner"><svg data-v-4f27ff84="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="btn-icon" fill="currentColor"><path d="M12 4.5C7.85786 4.5 4.5 7.85786 4.5 12C4.5 16.1421 7.85786 19.5 12 19.5C14.0667 19.5 15.9365 18.6654 17.2941 17.3125C17.6853 16.9226 18.3185 16.9237 18.7083 17.3149C19.0982 17.7061 19.0971 18.3393 18.7059 18.7291C16.9887 20.4404 14.6172 21.5 12 21.5C6.75329 21.5 2.5 17.2467 2.5 12C2.5 6.75329 6.75329 2.5 12 2.5C17.2467 2.5 21.5 6.75329 21.5 12C21.5 12.356 21.4804 12.7078 21.4421 13.0543C21.3814 13.6033 20.8872 13.9991 20.3382 13.9384C19.7893 13.8777 19.3935 13.3835 19.4542 12.8346C19.4844 12.5609 19.5 12.2825 19.5 12C19.5 7.85786 16.1421 4.5 12 4.5z" fill="currentColor"></path><path d="M17.2929 10.7929C17.6834 10.4024 18.3166 10.4024 18.7071 10.7929L20.5 12.5858L22.2929 10.7929C22.6834 10.4024 23.3166 10.4024 23.7071 10.7929C24.0976 11.1834 24.0976 11.8166 23.7071 12.2071L21.3839 14.5303C20.8957 15.0185 20.1043 15.0185 19.6161 14.5303L17.2929 12.2071C16.9024 11.8166 16.9024 11.1834 17.2929 10.7929z" fill="currentColor"></path></svg><div data-v-4f27ff84="" class="btn-text"><span data-v-4f27ff84="" class="btn-text-inner">刷新内容</span></div></div></div><div data-v-4f27ff84="" class="storage-box hidden"><div data-v-4f27ff84="" class="storable-items" style="display: none;"><a data-v-4f27ff84="" target="_blank" href="https://www.bilibili.com/blackboard/activity-xKR6yNjuJ6.html" class="primary-btn feedback"> 新版反馈 </a><a data-v-4f27ff84="" target="_blank" href="https://www.bilibili.com/v/customer-service" class="primary-btn"><svg data-v-4f27ff84="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><path d="M3.75 10.5C3.75 5.94365 7.44365 2.25 12 2.25C16.55635 2.25 20.25 5.94365 20.25 10.5C20.25 10.9142 19.9142 11.25 19.5 11.25C19.0858 11.25 18.75 10.9142 18.75 10.5C18.75 6.77208 15.72795 3.75 12 3.75C8.27208 3.75 5.25 6.77208 5.25 10.5C5.25 10.9142 4.91421 11.25 4.5 11.25C4.08579 11.25 3.75 10.9142 3.75 10.5z" fill="currentColor"></path><path d="M19.7837 16.27445C20.16715 16.43115 20.35095 16.869 20.19425 17.25245C19.4912 18.97275 17.94805 20.08875 16.53735 20.76425C15.13135 21.43745 13.69515 21.74995 13 21.74995C12.5858 21.74995 12.25 21.41415 12.25 20.99995C12.25 20.5857 12.5858 20.24995 13 20.24995C13.40985 20.24995 14.6224 20.0181 15.88955 19.4113C17.15195 18.8068 18.3063 17.9072 18.80575 16.685C18.9624 16.30155 19.4003 16.11775 19.7837 16.27445z" fill="currentColor"></path><path d="M10.25 21C10.25 20.3787 10.7537 19.875 11.375 19.875L12.625 19.875C13.2463 19.875 13.75 20.3787 13.75 21C13.75 21.6213 13.2463 22.125 12.625 22.125L11.375 22.125C10.7537 22.125 10.25 21.6213 10.25 21z" fill="currentColor"></path><path d="M2 12.75C2 11.2312 3.23122 10 4.75 10L5.5 10C6.60457 10 7.5 10.89545 7.5 12L7.5 15.5C7.5 16.60455 6.60457 17.5 5.5 17.5L4.75 17.5C3.23122 17.5 2 16.2688 2 14.75L2 12.75zM4.75 11.5C4.05965 11.5 3.5 12.05965 3.5 12.75L3.5 14.75C3.5 15.44035 4.05965 16 4.75 16L5.5 16C5.77614 16 6 15.77615 6 15.5L6 12C6 11.72385 5.77614 11.5 5.5 11.5L4.75 11.5z" fill="currentColor"></path><path d="M16.5 12C16.5 10.89545 17.39545 10 18.5 10L19.25 10C20.7688 10 22 11.2312 22 12.75L22 14.75C22 16.2688 20.7688 17.5 19.25 17.5L18.5 17.5C17.39545 17.5 16.5 16.60455 16.5 15.5L16.5 12zM18.5 11.Line truncated
|
||||
|
||||
<script type="text/javascript">window.__pinia=(function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,_,$,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,aA,aB,aC,aD,aE,aF,aG,aH,aI,aJ,aK,aL,aM,aN,aO,aP,aQ,aR,aS,aT,aU,aV,aW,aX,aY,aZ,a_,a$,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,bq,br,bs,bt,bu,bv,bw,bx,by,bz,bA,bB,bC,bD,bE,bF,bG,bH,bI,bJ,bK,bL,bM,bN,bO,bP,bQ,bR,bS,bT,bU,bV,bW,bX,bY,bZ,b_,b$,ca,cb,cc,cd,ce,cf,cg,ch,ci,cj,ck,cl,cm,cn,co,cp,cq,cr,cs,ct,cu){M.freshIdx=a;M.id=m;M.reportId="av_n_116752243492511";N.freshIdx=a;N.id=n;N.reportId="av_n_116753233348410";O.freshIdx=a;O.id=o;O.reportId="av_n_116748552570821";P.freshIdx=a;P.id=p;P.reportId="av_n_116748552508416";Q.freshIdx=a;Q.id=q;Q.reportId="av_n_116741472525660";R.freshIdx=a;R.id=r;R.reportId="av_n_116755330503886";S.freshIdx=a;S.id=s;S.reportId="av_n_116752293891019";T.freshIdx=a;T.id=t;T.reportId="av_n_116747294214059";U.freshIdx=a;U.id=u;U.reportId="av_n_116763031246456";V.freshIdx=a;V.id=v;V.reportId="av_n_116752377716135";return {index:{theme:"light",bmgDefDomain:b,homeVersion:L,smallest:d,xs_sm:d,xs:d,sm:d,md:d,lg:d,xl:d,xxl:d,headerBannerData:{id:371,style:a,rid:a,name:b,litpic:"http:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Farchive\u002F87a5d03581e326f4f818cab3212ce471d1f6a064.png",pic:"http:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Farchive\u002Fcc634ec77d2755641d5ed3f392c9b7e065a88aae.png",url:b,is_split_layer:e,split_layer:"{\"version\":\"1\",\"layers\":[{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002F7ba90052bb0a7d52ff2e2651c58325176823dab7.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{},\"blur\":{},\"opacity\":{\"wrap\":\"clamp\"},\"id\":0,\"name\":\"033bg\"},{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002F345b6e0fe3bcce70b4dda85984e6bd2e989ed383.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{\"offset\":[10,0]},\"blur\":{},\"opacity\":{\"wrap\":\"clamp\"},\"id\":1,\"name\":\"032云层\"},{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002F943d6df51d9698d2bbccfd1420a91e42c0bdb8ce.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{\"offset\":[12,0]},\"blur\":{},\"opacity\":{\"wrap\":\"clamp\"},\"id\":3,\"name\":\"030右边电线\"},{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002F7df8b2e7038b224ce684694d05e55ac537534f97.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{\"offset\":[10,5]},\"blur\":{},\"opacity\":{\"wrap\":\"clamp\"},\"id\":4,\"name\":\"029山\"},{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002Fd3a3efde2411a0a79421ca9e7a7fa5ffa6a377e8.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{\"offset\":[14,0]},\"blur\":{},\"opacity\":{\"wrap\":\"clamp\"},\"id\":6,\"name\":\"027右边树丛\"},{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002Fd6f0810cff9d297fb5de5dcd474b1303cc8efc56.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{\"offset\":[16,0]},\"blur\":{},\"opacity\":{\"wrap\":\"clamp\"},\"id\":7,\"name\":\"026水塘\"},{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002Fe3442b0c6d5b098a0577ae9a52fe3f862e253457.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{\"offset\":[12,2]},\"blur\":{},\"opacity\":{\"wrap\":\"clamp\"},\"id\":8,\"name\":\"025尾巴\"},{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002Fb45d1213022a666a43a8a826a05ba449ca14f5ae.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{\"offset\":[13,2]},\"blur\":{},\"opacity\":{\"wrap\":\"clamp\"},\"id\":9,\"name\":\"024二节\"},{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002Ffe1d49a21d9715906fd580a09f5dab5a4d8762b9.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{\"offset\":[10,2]},\"blur\":{},\"opacity\":{\"wrap\":\"clamp\"},\"id\":10,\"name\":\"023一节\"},{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002Faaed1e390b9cd7a7cf04da45cae135856b66df8f.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{\"offset\":[10,0]},\"blur\":{},\"opacity\":{\"wrap\":\"clamp\"},\"id\":11,\"name\":\"022石头\"},{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002F2b343db84d1754f9ad69a761d1a0043be367b134.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{\"offset\":[8,2]},\"blur\":{},\"opacity\":{\"wrap\":\"clamp\"},\"id\":12,\"name\":\"021尾巴\"},{\"resources\":[{\"src\":\"https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fvc\u002F3a951ea0bd0b2d1c7972f06fd087efdeef037094.png\",\"id\":0}],\"scale\":{\"initial\":0.45},\"rotate\":{},\"translate\":{\"offset\":[10,2]},\"blur\":{},\"opacity\":{\"wrap\":\Line truncated
|
||||
<script data-inject-polyfill="" src="https://www.bilibili.com/gentleman/polyfill.js?features=es2015%2Ces2016%2Ces2017%2Ces2018%2Ces2019%2Ces2020%2Ces2021%2Ces2022%2CglobalThis&flags=gated"></script>
|
||||
<script type="module" crossorigin="" src="//s1.hdslb.com/bfs/static/shanks/laputa-home/assets/index-ad80b630.js"></script>
|
||||
<script type="text/javascript" src="//s1.hdslb.com/bfs/cm/cm-sdk/static/js/bili-collect.js"></script>
|
||||
<script nomodule="">!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
|
||||
<script nomodule="" crossorigin="" id="vite-legacy-polyfill" src="//s1.hdslb.com/bfs/static/shanks/laputa-home/assets/polyfills-legacy-aeac6404.js"></script>
|
||||
<script nomodule="" crossorigin="" id="vite-legacy-entry" data-src="//s1.hdslb.com/bfs/static/shanks/laputa-home/assets/index-legacy-3dbe9f71.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
|
||||
|
||||
|
||||
<iframe id="DpbJcIT8jx" src="https://s1.hdslb.com/bfs/seed/jinkela/short/leader-election/iframe.html?leaderID=watchlaterpipwindow" style="display: none;"></iframe><iframe src="https://s1.hdslb.com/bfs/seed/jinkela/short/cols/iframe.html" style="display: none;" data-count="2" data-loaded="true"></iframe><div data-v-315489fe="" class="adcard"><div data-v-315489fe="" class="fixed-card-inner"><div data-v-315489fe="" class="palette-button-adcard is-bottom translucent" style="left: 660px;"><picture data-v-339aeb28="" data-v-315489fe="" class="v-img ad-img"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/activity-plat/static/20260615/e8d40a4dbad57d5373ac76d80151a459/0XTDmwYVBT.png@800w_512h.avif" type="image/avif"><source data-v-339aeb28="" srcset="//i1.hdslb.com/bfs/activity-plat/static/20260615/e8d40a4dbad57d5373ac76d80151a459/0XTDmwYVBT.png@800w_512h.webp" type="image/webp"><img data-v-339aeb28="" src="//i1.hdslb.com/bfs/activity-plat/static/20260615/e8d40a4dbad57d5373ac76d80151a459/0XTDmwYVBT.png@800w_512h" alt="card" loading="eager" onload="" onerror="
|
||||
typeof window.imgOnError === 'function' && window.imgOnError(this)
|
||||
"></picture><svg data-v-315489fe="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" class="exit-icon" fill="currentColor"><path d="M4.75075 4.75352C5.14127 4.36299 5.77444 4.36299 6.16496 4.75352L11.9984 10.587L17.8335 4.75188C18.224 4.36135 18.8572 4.36135 19.2477 4.75188C19.6383 5.1424 19.6383 5.77557 19.2477 6.16609L13.4126 12.0012L19.2464 17.835C19.637 18.2255 19.637 18.8587 19.2464 19.2492C18.8559 19.6397 18.2227 19.6397 17.8322 19.2492L11.9984 13.4154L6.16626 19.2476C5.77573 19.6381 5.14257 19.6381 4.75204 19.2476C4.36152 18.857 4.36152 18.2239 4.75204 17.8334L10.5842 12.0012L4.75075 6.16773C4.36022 5.77721 4.36022 5.14404 4.75075 4.75352z" fill="currentColor"></path></svg><div data-v-315489fe="" class="detail-button"> 查看详情 </div></div></div></div><script crossorigin="anonymous" type="text/javascript" src="//s1.hdslb.com/bfs/seed/jinkela/short/minntaki-wasm-sdk/bili-sc-sdk.umd.js"></script></body></html>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -8,3 +8,4 @@
|
||||
实现时可单独配置 tool_policy 白名单、更严格的 confirm 策略,
|
||||
并绑定专用 system_prompt 与较短上下文窗口。
|
||||
"""
|
||||
|
||||
@@ -37,6 +37,9 @@ def _build_main_agent_runtime():
|
||||
if cfg.system_prompt:
|
||||
_system_prompt = cfg.system_prompt
|
||||
_max_iterations = cfg.max_iterations
|
||||
# 根据 agent_config.json 中的 tools 列表过滤工具
|
||||
if cfg.tools:
|
||||
_tools = [t for t in _tools if t.name in cfg.tools]
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,24 +3,18 @@
|
||||
"name": "main",
|
||||
"system_prompt": "你是一个多模态助手,能够直接理解用户上传的图片、语音等内容。\n 【关键行为准则】\n - 你的默认行为是直接回答用户问题,不要调用任何工具。\n - 只有当某个信息是你**绝对无法从当前对话或上传内容中获取**,且该信息必须通过外部工具才能获得时,才允许调用工具。\n - 对于任何图片/音频输入,请始终**直接**利用你的视觉/听觉能力回答,**绝对不要**调用截图、OCR、日期查询等无关工具。\n - 如果你不确定是否需要工具,选择**不调用**,直接给出你能给出的最佳答案。\n - 违反上述规则是一种严重错误。",
|
||||
"max_iterations": 200,
|
||||
"llm": {
|
||||
"model": "qwen3.6-flash",
|
||||
"api_key": "sk-a44b226ec79a40fd8fcdbbaa74ba133e",
|
||||
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
"timeout": 60.0,
|
||||
"max_retries": 0
|
||||
},
|
||||
"llm_model_id": "a44b226e",
|
||||
"tools": [
|
||||
"todo_list",
|
||||
"gui_tool",
|
||||
"directory_listing",
|
||||
"web_search_class",
|
||||
"date_query_class",
|
||||
"screen_capture",
|
||||
"browser_control",
|
||||
"human_interaction",
|
||||
"software_control",
|
||||
"directory_listing"
|
||||
"browser_control"
|
||||
],
|
||||
"enabled": true,
|
||||
"role": "main"
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -17,7 +17,7 @@ from __future__ import annotations
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -40,23 +40,12 @@ class ModelConfig:
|
||||
max_retries: int = 0
|
||||
|
||||
|
||||
# ---------- LLM 配置结构(保留兼容) ----------
|
||||
@dataclass
|
||||
class LLMConfig:
|
||||
model: str = ""
|
||||
api_key: str = ""
|
||||
base_url: str = ""
|
||||
timeout: float = 60.0
|
||||
max_retries: int = 0
|
||||
|
||||
|
||||
@dataclass
|
||||
class AgentConfig:
|
||||
name: str = ""
|
||||
system_prompt: str = ""
|
||||
max_iterations: int = 200
|
||||
llm: LLMConfig = field(default_factory=LLMConfig) # 保留兼容
|
||||
llm_model_id: str = "" # 新:引用 models 列表中的模型 ID
|
||||
llm_model_id: str = "" # 引用 env_config.json models 列表中的模型 ID
|
||||
tools: list[str] = field(default_factory=list)
|
||||
enabled: bool = True
|
||||
role: str = "sub" # "main" | "sub"
|
||||
@@ -123,18 +112,10 @@ def load_agent_configs() -> list[AgentConfig]:
|
||||
|
||||
configs = []
|
||||
for item in raw:
|
||||
llm_raw = item.get("llm", {})
|
||||
configs.append(AgentConfig(
|
||||
name=item.get("name", ""),
|
||||
system_prompt=item.get("system_prompt", ""),
|
||||
max_iterations=item.get("max_iterations", 200),
|
||||
llm=LLMConfig(
|
||||
model=llm_raw.get("model", ""),
|
||||
api_key=llm_raw.get("api_key", ""),
|
||||
base_url=llm_raw.get("base_url", ""),
|
||||
timeout=llm_raw.get("timeout", 60.0),
|
||||
max_retries=llm_raw.get("max_retries", 0),
|
||||
),
|
||||
llm_model_id=item.get("llm_model_id", ""),
|
||||
tools=item.get("tools", []),
|
||||
enabled=item.get("enabled", True),
|
||||
@@ -217,12 +198,23 @@ def _default_tool_config() -> list[dict]:
|
||||
|
||||
|
||||
def load_tool_configs() -> list[ToolConfig]:
|
||||
"""从 tool_config.json 加载工具配置,无文件时返回默认。"""
|
||||
"""从 tool_config.json 加载工具配置,无文件时返回默认;有新工具时自动补入。"""
|
||||
raw = _read_json(TOOL_CONFIG_PATH)
|
||||
if not raw or not isinstance(raw, list):
|
||||
raw = _default_tool_config()
|
||||
_write_json(TOOL_CONFIG_PATH, raw)
|
||||
|
||||
# 补上代码中已注册但 JSON 中缺失的新工具
|
||||
registered = get_registered_tool_names()
|
||||
existing_names = {item.get("name", "") for item in raw}
|
||||
missing = [n for n in registered if n not in existing_names]
|
||||
if missing:
|
||||
defaults = {t["name"]: t for t in _default_tool_config()}
|
||||
for name in missing:
|
||||
if name in defaults:
|
||||
raw.append(defaults[name])
|
||||
_write_json(TOOL_CONFIG_PATH, raw)
|
||||
|
||||
configs = []
|
||||
for item in raw:
|
||||
configs.append(ToolConfig(
|
||||
@@ -317,7 +309,7 @@ def load_env_config() -> dict:
|
||||
raw["models"] = _default_models()
|
||||
_write_json(ENV_CONFIG_PATH, raw)
|
||||
return raw
|
||||
# 确保有 models 字段(向后兼容旧配置文件)
|
||||
# 确保有 models 字段
|
||||
if "models" not in raw or not isinstance(raw.get("models"), list):
|
||||
raw["models"] = _default_models()
|
||||
return raw
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
"models": [
|
||||
{
|
||||
"id": "a44b226e",
|
||||
"name": "阿里云Qwen",
|
||||
"name": "阿里云qwen3.6-35b-a3b",
|
||||
"api_key": "sk-a44b226ec79a40fd8fcdbbaa74ba133e",
|
||||
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
"model": "qwen3.6-flash",
|
||||
"model": "qwen3.6-35b-a3b",
|
||||
"timeout": 60,
|
||||
"max_retries": 0
|
||||
}
|
||||
|
||||
+14
-43
@@ -10,8 +10,6 @@
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import time
|
||||
from typing import Any, Callable
|
||||
|
||||
from langchain_core.messages import HumanMessage, ToolMessage
|
||||
@@ -22,43 +20,27 @@ from agent.utils.tool_call_utils import normalize_tool_call
|
||||
|
||||
|
||||
def make_call_model_node(model_with_tools: Any) -> Callable[[AgentState], dict]:
|
||||
"""创建调用已绑定工具的 LLM 的图节点闭包。
|
||||
|
||||
输入:
|
||||
model_with_tools: bind_tools 后的 LangChain 聊天模型。
|
||||
|
||||
输出:
|
||||
符合 LangGraph 节点签名的 ``call_model(state) -> {"messages": [AIMessage]}``。
|
||||
|
||||
系统定位:
|
||||
图中 ``agent`` 节点:读取 state.messages,调用模型,追加 AI 响应。
|
||||
|
||||
可扩展性:
|
||||
可包装为 async 节点;可在此统一记录 token 用量到 session_meta。
|
||||
"""
|
||||
"""创建调用已绑定工具的 LLM 的图节点闭包。"""
|
||||
|
||||
def call_model(state: AgentState) -> dict:
|
||||
response = model_with_tools.invoke(state["messages"])
|
||||
|
||||
usage = response.usage_metadata if hasattr(response, "usage_metadata") else {}
|
||||
if usage:
|
||||
input_tokens = usage.get("input_tokens", "?")
|
||||
output_tokens = usage.get("output_tokens", "?")
|
||||
total_tokens = usage.get("total_tokens", "?")
|
||||
print(f"[LLM] Token使用: input={input_tokens}, output={output_tokens}, total={total_tokens}")
|
||||
|
||||
if hasattr(response, "tool_calls") and response.tool_calls:
|
||||
for tc in response.tool_calls:
|
||||
tc_normalized = normalize_tool_call(tc)
|
||||
tool_name = tc_normalized.get("name", "unknown")
|
||||
tool_args = tc_normalized.get("args", {})
|
||||
print(f"[LLM] 工具调用: {tool_name}, 参数: {tool_args}")
|
||||
# 实时记录工具调用
|
||||
try:
|
||||
from agent.history.tool_call_recorder import record_tool_call_live, get_current_session
|
||||
sid = get_current_session()
|
||||
if sid:
|
||||
record_tool_call_live(sid, tool_name, tool_args)
|
||||
if tool_name == "todo_list":
|
||||
try:
|
||||
from agent.tools.tool import update_todo_store_from_tool_args
|
||||
update_todo_store_from_tool_args(sid, tool_args)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -74,28 +56,17 @@ def process_tool_artifact(state: AgentState) -> dict:
|
||||
content_preview = str(last_msg.content)
|
||||
if len(content_preview) > 200:
|
||||
content_preview = content_preview[:200] + "..."
|
||||
print(f"[Tool] 工具返回 (id={last_msg.tool_call_id}): {content_preview}")
|
||||
|
||||
# 实时记录工具返回结果
|
||||
try:
|
||||
from agent.history.tool_call_recorder import record_tool_result_live, get_current_session
|
||||
sid = get_current_session()
|
||||
if sid:
|
||||
img_bytes = None
|
||||
if last_msg.artifact:
|
||||
if isinstance(last_msg.artifact, dict) and "image_bytes" in last_msg.artifact:
|
||||
img_bytes = last_msg.artifact["image_bytes"]
|
||||
elif isinstance(last_msg.artifact, list):
|
||||
for item in last_msg.artifact:
|
||||
if isinstance(item, dict) and "image_bytes" in item:
|
||||
img_bytes = item["image_bytes"]
|
||||
break
|
||||
elif isinstance(item, bytes):
|
||||
img_bytes = item
|
||||
break
|
||||
elif isinstance(last_msg.artifact, bytes):
|
||||
img_bytes = last_msg.artifact
|
||||
record_tool_result_live(sid, last_msg.name or "unknown", str(last_msg.content) if last_msg.content else "", img_bytes)
|
||||
record_tool_result_live(
|
||||
sid,
|
||||
last_msg.name or "unknown",
|
||||
str(last_msg.content) if last_msg.content else "",
|
||||
last_msg.artifact,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -140,4 +111,4 @@ def process_tool_artifact(state: AgentState) -> dict:
|
||||
content_parts[-1]["text"] += f"(其中 {failed_count} 张图片处理失败,请忽略)"
|
||||
|
||||
human_msg = HumanMessage(content=content_parts, additional_kwargs={"synthetic": True})
|
||||
return {"messages": [human_msg]}
|
||||
return {"messages": [human_msg]}
|
||||
@@ -471,6 +471,6 @@ def continue_after_human_action(
|
||||
chat_history.append({"role": "assistant", "content": content, "meta": {"output_type": output_type, "autoplay": False}})
|
||||
_persist_messages_from_pending_turn(session_id, persisted_messages, pending)
|
||||
if session_id:
|
||||
save_turn(session_id, pending.get("user_content") or {}, persisted_messages)
|
||||
save_turn(session_id, f"human_{approval_id}", {"role": "user", "content": pending.get("user_content") or ""}, persisted_messages)
|
||||
sync_manifest_from_chat_history(session_id, chat_history, "")
|
||||
return chat_history
|
||||
@@ -11,7 +11,7 @@
|
||||
"name": "date_query_class",
|
||||
"display_name": "DateQuery",
|
||||
"description": "一个日期查询工具,可以返回当前的日期信息。仅当需要查询当前时间时调用,其余任何情况严禁调用。",
|
||||
"permission": "direct",
|
||||
"permission": "confirm",
|
||||
"auto_execute_rules": [],
|
||||
"enabled": true
|
||||
},
|
||||
@@ -27,8 +27,14 @@
|
||||
"name": "browser_control",
|
||||
"display_name": "BrowserControl",
|
||||
"description": "统一控制浏览器:打开/关闭浏览器、刷新/关闭标签页、打开指定 URL。",
|
||||
"permission": "direct",
|
||||
"auto_execute_rules": [],
|
||||
"permission": "confirm",
|
||||
"auto_execute_rules": [
|
||||
{
|
||||
"parameter": "action",
|
||||
"operator": "equals",
|
||||
"value": "open"
|
||||
}
|
||||
],
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
@@ -43,10 +49,24 @@
|
||||
"name": "software_control",
|
||||
"display_name": "SoftwareControl",
|
||||
"description": "操作本机已安装的软件。工具会自动扫描 Windows 开始菜单快捷方式、桌面快捷方式和 App Paths,建立软件索引后根据用户输入做模糊匹配。action=open 时打开软件;action=close 时仅做正常关闭;action=force_close 时才允许在正常关闭失败后强制结束进程。仅当用户明确要求打开、关闭或强制关闭某个本机软件时调用。",
|
||||
"permission": "direct",
|
||||
"permission": "confirm",
|
||||
"auto_execute_rules": [],
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "rag_tool",
|
||||
"display_name": "RAGTool",
|
||||
"description": "个人知识库管理工具,支持增删查及列出所有来源操作。",
|
||||
"permission": "confirm",
|
||||
"auto_execute_rules": [
|
||||
{
|
||||
"parameter": "action_type",
|
||||
"operator": "equals",
|
||||
"value": "query"
|
||||
}
|
||||
],
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "directory_listing",
|
||||
"display_name": "DirectoryListing",
|
||||
@@ -54,5 +74,21 @@
|
||||
"permission": "direct",
|
||||
"auto_execute_rules": [],
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"display_name": "TodoList",
|
||||
"description": "可选的任务管理工具。当你需要拆分复杂任务为多个步骤并跟踪进度时调用。输入 steps (list[str]) 为每一步的描述,step (int) 为已完成的步骤索引(从0开始)。任务默认状态为 pending,根据 step 自动标记完成。工具返回当前 todo list 文本。仅在需要跟踪多步骤任务进度时调用,简单任务不需要调用。",
|
||||
"permission": "direct",
|
||||
"auto_execute_rules": [],
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "gui_tool",
|
||||
"display_name": "GUITool",
|
||||
"description": "GUI automation tool. Visually locates desktop elements from a screenshot, maps coordinates to the real screen, and executes pyautogui actions. Supported actions: click, type, drag_and_drop, scroll, ho",
|
||||
"permission": "direct",
|
||||
"auto_execute_rules": [],
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"version": 3,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "调用一次todolist生成一份详细的做西红柿炒鸡蛋计划"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "已为您生成详细的**西红柿炒鸡蛋**制作计划,共分为8个步骤:\n\n1. **准备食材**:2-3个鸡蛋、2个中等大小西红柿、葱花少许、食用油适量、盐适量、糖少许(可选)\n2. **处理食材**:将鸡蛋打入碗中,加少许盐打散;西红柿洗净切块;葱切成葱花备用\n3. **热锅倒油**:开中火加热炒锅,倒入适量食用油,待油微热\n4. **炒鸡蛋**:倒入蛋液,用筷子或铲子快速划散,炒至凝固后盛出备用\n5. **炒西红柿**:锅中留底油,放入西红柿块翻炒,可用铲子压一压帮助出汁,炒至软烂\n6. **调味**:根据口味加入适量盐和少许糖提鲜,翻炒均匀\n7. **混合**:将炒好的鸡蛋倒回锅中,与西红柿翻炒均匀,让鸡蛋吸收汤汁\n8. **出锅**:撒上葱花点缀,装盘即可享用\n\n祝您烹饪愉快!如果需要标记某一步骤完成,请告诉我。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "你的上下文中有什么"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "需要你确认后继续执行。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": {
|
||||
"id": "b5de8b0742564742ba20ec9df9803b51",
|
||||
"type": "tool_call",
|
||||
"title": "待确认执行工具:rag_tool",
|
||||
"tool_name": "rag_tool",
|
||||
"args": {
|
||||
"name": "rag_tool",
|
||||
"args": {
|
||||
"action_type": "list_sources"
|
||||
}
|
||||
},
|
||||
"instruction": "{\n \"name\": \"rag_tool\",\n \"args\": {\n \"action_type\": \"list_sources\"\n }\n}",
|
||||
"options": [
|
||||
"approve",
|
||||
"reject"
|
||||
],
|
||||
"resolved": true
|
||||
},
|
||||
"agent_mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "已拒绝,本次待确认操作不会执行。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"version": 3,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "帮我关掉DeepSeek开放平台的标签页"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"assistant": {
|
||||
"text": "已经帮您关闭了 DeepSeek 开放平台的标签页。现在浏览器显示的是 minor - Chatbot 页面。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null,
|
||||
"pending_action": null,
|
||||
"agent_mode": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,9 +7,8 @@
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import base64
|
||||
import json
|
||||
import time
|
||||
import threading
|
||||
from contextvars import ContextVar
|
||||
@@ -25,6 +24,9 @@ TOOL_CALLING_ROOT = _HISTORY_ROOT / "tool_calling"
|
||||
# ---------- 当前 turn 上下文 ----------
|
||||
_current_turn_id: ContextVar[str] = ContextVar("current_turn_id", default="")
|
||||
_current_session_id: ContextVar[str] = ContextVar("current_session_id", default="")
|
||||
_context_fallback_lock = threading.Lock()
|
||||
_current_turn_fallback = ""
|
||||
_current_session_fallback = ""
|
||||
|
||||
# ---------- 实时记录(线程安全) ----------
|
||||
_live_lock = threading.Lock()
|
||||
@@ -32,19 +34,33 @@ _live_records: dict[str, list[dict[str, Any]]] = {} # key = session_id
|
||||
|
||||
|
||||
def set_current_turn(turn_id: str) -> None:
|
||||
global _current_turn_fallback
|
||||
_current_turn_id.set(turn_id)
|
||||
with _context_fallback_lock:
|
||||
_current_turn_fallback = turn_id
|
||||
|
||||
|
||||
def get_current_turn() -> str:
|
||||
return _current_turn_id.get()
|
||||
turn_id = _current_turn_id.get()
|
||||
if turn_id:
|
||||
return turn_id
|
||||
with _context_fallback_lock:
|
||||
return _current_turn_fallback
|
||||
|
||||
|
||||
def set_current_session(session_id: str) -> None:
|
||||
global _current_session_fallback
|
||||
_current_session_id.set(session_id)
|
||||
with _context_fallback_lock:
|
||||
_current_session_fallback = session_id
|
||||
|
||||
|
||||
def get_current_session() -> str:
|
||||
return _current_session_id.get()
|
||||
session_id = _current_session_id.get()
|
||||
if session_id:
|
||||
return session_id
|
||||
with _context_fallback_lock:
|
||||
return _current_session_fallback
|
||||
|
||||
|
||||
def start_live_turn(session_id: str) -> str:
|
||||
@@ -63,6 +79,7 @@ def record_tool_call_live(session_id: str, name: str, args: dict[str, Any]) -> N
|
||||
_live_records[session_id] = []
|
||||
_live_records[session_id].append({
|
||||
"name": name,
|
||||
"args": _json_safe(args),
|
||||
"args_summary": _short_args(args),
|
||||
"status": "running",
|
||||
"result_text": "",
|
||||
@@ -70,20 +87,63 @@ def record_tool_call_live(session_id: str, name: str, args: dict[str, Any]) -> N
|
||||
})
|
||||
|
||||
|
||||
def record_tool_result_live(session_id: str, name: str, result_text: str,
|
||||
image_bytes: bytes | None = None) -> None:
|
||||
"""图节点中调用:记录工具返回结果。"""
|
||||
def record_tool_result_live(
|
||||
session_id: str,
|
||||
name: str,
|
||||
result_text: str,
|
||||
artifact: Any | None = None,
|
||||
) -> None:
|
||||
"""记录工具返回结果,供前端实时展示文本与图片预览。"""
|
||||
with _live_lock:
|
||||
records = _live_records.get(session_id, [])
|
||||
for rec in reversed(records):
|
||||
if rec["name"] == name and rec["status"] == "running":
|
||||
rec["status"] = "done"
|
||||
rec["result_text"] = (result_text or "")[:500]
|
||||
if image_bytes:
|
||||
rec["image_base64"] = base64.standard_b64encode(image_bytes).decode("ascii")
|
||||
images = _artifact_image_data_urls(artifact)
|
||||
if images:
|
||||
rec["result_images"] = images
|
||||
return
|
||||
|
||||
|
||||
def _artifact_image_data_urls(artifact: Any | None) -> list[str]:
|
||||
if not artifact:
|
||||
return []
|
||||
|
||||
images: list[str] = []
|
||||
image_bytes_list: list[bytes] = []
|
||||
|
||||
def collect(item: Any) -> None:
|
||||
if isinstance(item, bytes):
|
||||
image_bytes_list.append(item)
|
||||
return
|
||||
if not isinstance(item, dict):
|
||||
return
|
||||
if isinstance(item.get("image_bytes"), bytes):
|
||||
image_bytes_list.append(item["image_bytes"])
|
||||
for key in ("image_url", "url", "data_url"):
|
||||
value = item.get(key)
|
||||
if isinstance(value, str) and (value.startswith("data:image/") or value.startswith("http")):
|
||||
images.append(value)
|
||||
image_url = item.get("image_url")
|
||||
if isinstance(image_url, dict):
|
||||
value = image_url.get("url")
|
||||
if isinstance(value, str) and (value.startswith("data:image/") or value.startswith("http")):
|
||||
images.append(value)
|
||||
|
||||
if isinstance(artifact, list):
|
||||
for item in artifact:
|
||||
collect(item)
|
||||
else:
|
||||
collect(artifact)
|
||||
|
||||
images.extend(
|
||||
"data:image/png;base64," + base64.standard_b64encode(img).decode("ascii")
|
||||
for img in image_bytes_list
|
||||
)
|
||||
return images
|
||||
|
||||
|
||||
def end_live_turn(session_id: str) -> list[dict[str, Any]]:
|
||||
"""结束实时记录并返回该 turn 所有记录。"""
|
||||
set_current_turn("")
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"session_id": "20260618_111759",
|
||||
"turn_id": "20260618_111855",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "调用一次todolist生成一份详细的做西红柿炒鸡蛋计划"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:2-3个鸡蛋、2个中等大小西红柿、葱花少许、食用油适量、盐适量、糖少许(可选)",
|
||||
"处理食材:将鸡蛋打入碗中,加少许盐打散;西红柿洗净切块;葱切成葱花备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油,待油微热",
|
||||
"炒鸡蛋:倒入蛋液,用筷子或铲子快速划散,炒至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿块翻炒,可用铲子压一压帮助出汁,炒至软烂",
|
||||
"调味:根据口味加入适量盐和少许糖提鲜,翻炒均匀",
|
||||
"混合:将炒好的鸡蛋倒回锅中,与西红柿翻炒均匀,让鸡蛋吸收汤汁",
|
||||
"出锅:撒上葱花点缀,装盘即可享用"
|
||||
]
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a2-3\\u4e2a\\u9e21\\u86cb\\u30012\\u4e2a\\u4e2d\\u7b49\\u5927\\u5c0f\\u897f\\u7ea2\\u67ff\\u3001\\u8471\\u82b1\\u5c11\\u8bb8\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\\u3001\\u76d0\\u9002\\u91cf\\u3001\\u7cd6\\u5c11\\u8bb8\\uff08\\u53ef\\u9009\\uff09\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\uff0c\\u52a0\\u5c11\\u8bb8\\u76d0\\u6253\\u6563\\uff1b\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u8471\\u5207\\u6210\\u8471\\u82b1\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\uff0c\\u5f85\\u6cb9\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u7528\\u7b77\\u5b50\\u6216\\u94f2\\u5b50\\u5feb\\u901f\\u5212\\u6563\\uff0c\\u7092\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u5757\\u7ffb\\u7092\\uff0c\\u53ef\\u7528\\u94f2\\u5b50\\u538b\\u4e00\\u538b\\u5e2e\\u52a9\\u51fa\\u6c41\\uff0c\\u7092\\u81f3\\u8f6f\\u70c2\", \"\\u8c03\\u5473\\uff1a\\u6839\\u636e\\u53e3\\u5473\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\u548c\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff0c\\u7ffb\\u7092\\u5747\\u5300\", \"\\u6df7\\u5408\\uff1a\\u5c06\\u7092\\u597d\\u7684\\u9e21\\u86cb\\u5012\\u56de\\u9505\\u4e2d\\uff0c\\u4e0e\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u5747\\u5300\\uff0c\\u8ba9\\u9e21\\u86cb\\u5438\\u6536\\u6c64\\u6c41\", \"\\u51fa\\u9505\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u88c5\\u76d8\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": []}}\n[Todo List]\n- [1/8] [Pending]: 准备食材:2-3个鸡蛋、2个中等大小西红柿、葱花少许、食用油适量、盐适量、糖少许(可选)\n- [2/8] [Pending]: 处理食材:将鸡蛋打入碗中,加少许盐打散;西红柿洗净切块;葱切成葱花备用\n- [3/8] [Pending]: 热锅倒油:开中火加热炒锅,倒入适量食用油,待油微热\n- [4/8] [Pending]: 炒鸡蛋:倒入蛋液,用筷子或铲子快速划散,炒至凝固后盛出备用\n- [5/8] [Pending]: 炒西红柿:锅中留底油,放入西红柿块翻炒,可用铲子压一压帮助出汁,炒至软烂\n- [6/8] [Pending]: 调味:根据口味加入适量盐和少许糖提鲜,翻炒均匀\n- [7/8] [Pending]: 混合:将炒好的鸡蛋倒回锅中,与西红柿翻炒均匀,让鸡蛋吸收汤汁\n- [8/8] [Pending]: 出锅:撒上葱花点缀,装盘即可享用",
|
||||
"result_files": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "已为您生成详细的**西红柿炒鸡蛋**制作计划,共分为8个步骤:\n\n1. **准备食材**:2-3个鸡蛋、2个中等大小西红柿、葱花少许、食用油适量、盐适量、糖少许(可选)\n2. **处理食材**:将鸡蛋打入碗中,加少许盐打散;西红柿洗净切块;葱切成葱花备用\n3. **热锅倒油**:开中火加热炒锅,倒入适量食用油,待油微热\n4. **炒鸡蛋**:倒入蛋液,用筷子或铲子快速划散,炒至凝固后盛出备用\n5. **炒西红柿**:锅中留底油,放入西红柿块翻炒,可用铲子压一压帮助出汁,炒至软烂\n6. **调味**:根据口味加入适量盐和少许糖提鲜,翻炒均匀\n7. **混合**:将炒好的鸡蛋倒回锅中,与西红柿翻炒均匀,让鸡蛋吸收汤汁\n8. **出锅**:撒上葱花点缀,装盘即可享用\n\n祝您烹饪愉快!如果需要标记某一步骤完成,请告诉我。",
|
||||
"timestamp": "20260618_111855"
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"session_id": "20260618_112443",
|
||||
"turn_id": "20260618_112445",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "调用一次todolist生成一份详细的做西红柿炒鸡蛋计划"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:2-3个鸡蛋、2-3个中等大小西红柿、葱花适量",
|
||||
"准备调料:食用油、盐、白糖(可选,用于提鲜)、番茄酱(可选)",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅凉油:开中火加热炒锅,倒入适量食用油",
|
||||
"炒鸡蛋:油热后倒入蛋液,快速划散至凝固,盛出备用",
|
||||
"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁变软",
|
||||
"调味:加入适量盐和少许白糖,喜欢浓郁口感可加一勺番茄酱",
|
||||
"混合翻炒:将炒好的鸡蛋倒回锅中,与西红柿均匀翻炒混合",
|
||||
"出锅装盘:撒上葱花点缀,即可享用美味的西红柿炒鸡蛋"
|
||||
]
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a2-3\\u4e2a\\u9e21\\u86cb\\u30012-3\\u4e2a\\u4e2d\\u7b49\\u5927\\u5c0f\\u897f\\u7ea2\\u67ff\\u3001\\u8471\\u82b1\\u9002\\u91cf\", \"\\u51c6\\u5907\\u8c03\\u6599\\uff1a\\u98df\\u7528\\u6cb9\\u3001\\u76d0\\u3001\\u767d\\u7cd6\\uff08\\u53ef\\u9009\\uff0c\\u7528\\u4e8e\\u63d0\\u9c9c\\uff09\\u3001\\u756a\\u8304\\u9171\\uff08\\u53ef\\u9009\\uff09\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u51c9\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u6cb9\\u70ed\\u540e\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\uff0c\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u518d\\u52a0\\u5c11\\u8bb8\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u5757\\u7ffb\\u7092\\u81f3\\u51fa\\u6c41\\u53d8\\u8f6f\", \"\\u8c03\\u5473\\uff1a\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\u548c\\u5c11\\u8bb8\\u767d\\u7cd6\\uff0c\\u559c\\u6b22\\u6d53\\u90c1\\u53e3\\u611f\\u53ef\\u52a0\\u4e00\\u52fa\\u756a\\u8304\\u9171\", \"\\u6df7\\u5408\\u7ffb\\u7092\\uff1a\\u5c06\\u7092\\u597d\\u7684\\u9e21\\u86cb\\u5012\\u56de\\u9505\\u4e2d\\uff0c\\u4e0e\\u897f\\u7ea2\\u67ff\\u5747\\u5300\\u7ffb\\u7092\\u6df7\\u5408\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\\u7f8e\\u5473\\u7684\\u897f\\u7ea2\\u67ff\\u7092\\u9e21\\u86cb\"], \"done_steps\": []}}\n[Todo List]\n- [1/9] [Pending]: 准备食材:2-3个鸡蛋、2-3个中等大小西红柿、葱花适量\n- [2/9] [Pending]: 准备调料:食用油、盐、白糖(可选,用于提鲜)、番茄酱(可选)\n- [3/9] [Pending]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [4/9] [Pending]: 热锅凉油:开中火加热炒锅,倒入适量食用油\n- [5/9] [Pending]: 炒鸡蛋:油热后倒入蛋液,快速划散至凝固,盛出备用\n- [6/9] [Pending]: 炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁变软\n- [7/9] [Pending]: 调味:加入适量盐和少许白糖,喜欢浓郁口感可加一勺番茄酱\n- [8/9] [Pending]: 混合翻炒:将炒好的鸡蛋倒回锅中,与西红柿均匀翻炒混合\n- [9/9] [Pending]: 出锅装盘:撒上葱花点缀,即可享用美味的西红柿炒鸡蛋",
|
||||
"result_files": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "已为您生成详细的西红柿炒鸡蛋制作计划,共9个步骤:\n\n1. **准备食材**:2-3个鸡蛋、2-3个中等大小西红柿、葱花适量\n2. **准备调料**:食用油、盐、白糖(可选,用于提鲜)、番茄酱(可选)\n3. **处理食材**:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n4. **热锅凉油**:开中火加热炒锅,倒入适量食用油\n5. **炒鸡蛋**:油热后倒入蛋液,快速划散至凝固,盛出备用\n6. **炒西红柿**:锅中再加少许油,放入西红柿块翻炒至出汁变软\n7. **调味**:加入适量盐和少许白糖,喜欢浓郁口感可加一勺番茄酱\n8. **混合翻炒**:将炒好的鸡蛋倒回锅中,与西红柿均匀翻炒混合\n9. **出锅装盘**:撒上葱花点缀,即可享用美味的西红柿炒鸡蛋\n\n祝您烹饪愉快!",
|
||||
"timestamp": "20260618_112445"
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
{
|
||||
"session_id": "20260618_113558",
|
||||
"turn_id": "20260618_113615",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "调用一次todolist生成一份详细的做西红柿炒鸡蛋计划,并逐步更新这份计划"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
]
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": []}}\n[Todo List]\n- [1/7] [Pending]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Pending]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Pending]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Pending]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Pending]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Pending]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Pending]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Pending]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Pending]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 2
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Pending]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Pending]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Pending]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 3
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1, 2]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Done]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Pending]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Pending]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 4
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1, 2, 3]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Done]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Done]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Pending]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 5
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1, 2, 3, 4]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Done]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Done]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Done]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Pending]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 6
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1, 2, 3, 4, 5]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Done]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Done]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Done]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Done]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Pending]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": [
|
||||
"准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量",
|
||||
"处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用",
|
||||
"热锅倒油:开中火加热炒锅,倒入适量食用油至微热",
|
||||
"炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用",
|
||||
"炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)",
|
||||
"混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁",
|
||||
"出锅装盘:撒上葱花点缀,即可享用"
|
||||
],
|
||||
"done_step": 7
|
||||
},
|
||||
"result_text": "{\"__todo_list__\": true, \"data\": {\"steps\": [\"\\u51c6\\u5907\\u98df\\u6750\\uff1a\\u897f\\u7ea2\\u67ff2-3\\u4e2a\\uff0c\\u9e21\\u86cb3\\u4e2a\\uff0c\\u8471\\u82b1\\u5c11\\u8bb8\\uff0c\\u76d0\\u3001\\u7cd6\\u3001\\u98df\\u7528\\u6cb9\\u9002\\u91cf\", \"\\u5904\\u7406\\u98df\\u6750\\uff1a\\u5c06\\u897f\\u7ea2\\u67ff\\u6d17\\u51c0\\u5207\\u5757\\uff1b\\u9e21\\u86cb\\u6253\\u5165\\u7897\\u4e2d\\u6405\\u6563\\u5907\\u7528\", \"\\u70ed\\u9505\\u5012\\u6cb9\\uff1a\\u5f00\\u4e2d\\u706b\\u52a0\\u70ed\\u7092\\u9505\\uff0c\\u5012\\u5165\\u9002\\u91cf\\u98df\\u7528\\u6cb9\\u81f3\\u5fae\\u70ed\", \"\\u7092\\u9e21\\u86cb\\uff1a\\u5012\\u5165\\u86cb\\u6db2\\uff0c\\u5feb\\u901f\\u5212\\u6563\\u81f3\\u51dd\\u56fa\\u540e\\u76db\\u51fa\\u5907\\u7528\", \"\\u7092\\u897f\\u7ea2\\u67ff\\uff1a\\u9505\\u4e2d\\u7559\\u5e95\\u6cb9\\uff0c\\u653e\\u5165\\u897f\\u7ea2\\u67ff\\u7ffb\\u7092\\u51fa\\u6c41\\uff08\\u53ef\\u52a0\\u5c11\\u8bb8\\u7cd6\\u63d0\\u9c9c\\uff09\", \"\\u6df7\\u5408\\u8c03\\u5473\\uff1a\\u5012\\u5165\\u7092\\u597d\\u7684\\u9e21\\u86cb\\uff0c\\u52a0\\u5165\\u9002\\u91cf\\u76d0\\uff0c\\u7ffb\\u7092\\u5747\\u5300\\u4f7f\\u9e21\\u86cb\\u5438\\u6ee1\\u6c64\\u6c41\", \"\\u51fa\\u9505\\u88c5\\u76d8\\uff1a\\u6492\\u4e0a\\u8471\\u82b1\\u70b9\\u7f00\\uff0c\\u5373\\u53ef\\u4eab\\u7528\"], \"done_steps\": [0, 1, 2, 3, 4, 5, 6]}}\n[Todo List]\n- [1/7] [Done]: 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [2/7] [Done]: 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [3/7] [Done]: 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [4/7] [Done]: 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [5/7] [Done]: 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [6/7] [Done]: 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [7/7] [Done]: 出锅装盘:撒上葱花点缀,即可享用",
|
||||
"result_files": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "太棒了!西红柿炒鸡蛋计划已全部完成。\n\n**最终状态:**\n- [x] 准备食材:西红柿2-3个,鸡蛋3个,葱花少许,盐、糖、食用油适量\n- [x] 处理食材:将西红柿洗净切块;鸡蛋打入碗中搅散备用\n- [x] 热锅倒油:开中火加热炒锅,倒入适量食用油至微热\n- [x] 炒鸡蛋:倒入蛋液,快速划散至凝固后盛出备用\n- [x] 炒西红柿:锅中留底油,放入西红柿翻炒出汁(可加少许糖提鲜)\n- [x] 混合调味:倒入炒好的鸡蛋,加入适量盐,翻炒均匀使鸡蛋吸满汤汁\n- [x] 出锅装盘:撒上葱花点缀,即可享用\n\n现在你可以享受这道美味的家常菜了!🍅🥚",
|
||||
"timestamp": "20260618_113615"
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"session_id": "20260618_120438",
|
||||
"turn_id": "20260618_120441",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "调用一次todolist生成一份详细的做西红柿炒鸡蛋计划,并逐步更新这份计划"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": -1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': -1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": -1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': -1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
},
|
||||
{
|
||||
"name": "todo_list",
|
||||
"args": {
|
||||
"steps": "[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]",
|
||||
"done_step": 1
|
||||
},
|
||||
"result_text": "Error invoking tool 'todo_list' with kwargs {'steps': '[\"准备食材:2-3个西红柿、2-3个鸡蛋、葱花、盐、糖、食用油\", \"处理西红柿:洗净,去蒂,切成小块备用\", \"打散鸡蛋:将鸡蛋打入碗中,加入少许盐,充分搅打均匀\", \"热锅凉油(炒蛋):锅中倒适量油,油热后倒入蛋液,凝固后盛出备用\", \"炒西红柿:锅中再加少许油,放入西红柿块翻炒至出汁软烂\", \"调味:根据口味加入适量的盐和糖,翻炒均匀\", \"混合:将之前炒好的鸡蛋倒回锅中,与西红柿混合翻炒均匀\", \"出锅:撒上葱花,装盘即可\"]', 'done_step': 1} with error:\n steps: Input should be a valid list\n Please fix the error and try again.",
|
||||
"result_files": []
|
||||
}
|
||||
],
|
||||
"assistant_response": "",
|
||||
"timestamp": "20260618_120441"
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"session_id": "20260618_173653",
|
||||
"turn_id": "20260618_173727",
|
||||
"user_message": {
|
||||
"role": "user",
|
||||
"content": "帮我关掉DeepSeek开放平台的标签页"
|
||||
},
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "gui_tool",
|
||||
"args": {
|
||||
"action": "click",
|
||||
"element_description": "DeepSeek开放平台的标签页关闭按钮(X)"
|
||||
},
|
||||
"result_text": "Clicked target. grounded (308,21)",
|
||||
"result_files": [
|
||||
"_artifact_call_fd502781107945c7a6880635_0.png"
|
||||
]
|
||||
}
|
||||
],
|
||||
"assistant_response": "已经帮您关闭了 DeepSeek 开放平台的标签页。现在浏览器显示的是 minor - Chatbot 页面。",
|
||||
"timestamp": "20260618_173727"
|
||||
}
|
||||
Binary file not shown.
@@ -1,5 +1,6 @@
|
||||
from .tool import *
|
||||
from .tool_software_control import SoftwareControl
|
||||
from .gui_tool import GUITool
|
||||
from .tool_rag import RAGTool
|
||||
|
||||
_all_tools = [
|
||||
@@ -12,6 +13,7 @@ _all_tools = [
|
||||
RAGTool(),
|
||||
DirectoryListing(),
|
||||
TodoList(),
|
||||
GUITool(),
|
||||
]
|
||||
|
||||
# 注册所有可用工具名称(供前端配置面板展示),从 _all_tools 实例推导
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+314
-1
@@ -1 +1,314 @@
|
||||
from agent.utils.env_utils import GROUNDING_WIDTH, GROUNDING_HEIGHT
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import io
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from typing import Any, Literal, Optional
|
||||
|
||||
import pyautogui
|
||||
from langchain.tools import BaseTool
|
||||
from langchain_core.messages import HumanMessage
|
||||
from PIL import Image
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from agent.core.llm import llm
|
||||
from agent.utils.env_utils import GROUNDING_HEIGHT, GROUNDING_WIDTH
|
||||
|
||||
|
||||
GUIAction = Literal[
|
||||
"click",
|
||||
"type",
|
||||
"drag_and_drop",
|
||||
"scroll",
|
||||
"hotkey",
|
||||
"hold_and_press",
|
||||
"wait",
|
||||
]
|
||||
|
||||
|
||||
class GUIToolInput(BaseModel):
|
||||
# 将 action 改为可选,未提供时直接返回屏幕截图
|
||||
action: Optional[GUIAction] = Field(
|
||||
default=None, # ← 允许为 None
|
||||
description="GUI action to execute. If None or empty, returns current screenshot.",
|
||||
)
|
||||
element_description: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Natural-language description of the target screen element for grounded actions.",
|
||||
)
|
||||
ending_description: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Natural-language description of the drag end target.",
|
||||
)
|
||||
text: str = Field(default="", description="Text to type.")
|
||||
overwrite: bool = Field(default=False, description="Select all existing text before typing.")
|
||||
enter: bool = Field(default=False, description="Press Enter after typing.")
|
||||
num_clicks: int = Field(default=1, description="Number of mouse clicks.")
|
||||
button_type: Literal["left", "middle", "right"] = Field(default="left", description="Mouse button.")
|
||||
hold_keys: list[str] = Field(default_factory=list, description="Modifier keys to hold while clicking or dragging.")
|
||||
keys: list[str] = Field(default_factory=list, description="Hotkey keys or keys to press.")
|
||||
press_keys: list[str] = Field(default_factory=list, description="Keys to press while holding hold_keys.")
|
||||
clicks: int = Field(default=0, description="Scroll click amount. Negative means down.")
|
||||
shift: bool = Field(default=False, description="Use horizontal scrolling when true.")
|
||||
wait_seconds: float = Field(default=0.5, description="Seconds to wait.")
|
||||
|
||||
|
||||
def _screenshot_png() -> tuple[bytes, tuple[int, int]]:
|
||||
image = pyautogui.screenshot()
|
||||
width, height = image.size
|
||||
buf = io.BytesIO()
|
||||
image.save(buf, format="PNG")
|
||||
return buf.getvalue(), (width, height)
|
||||
|
||||
|
||||
def _resize_for_grounding(image_bytes: bytes) -> bytes:
|
||||
image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
|
||||
image = image.resize((GROUNDING_WIDTH, GROUNDING_HEIGHT), Image.Resampling.LANCZOS)
|
||||
buf = io.BytesIO()
|
||||
image.save(buf, format="PNG")
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def _image_data_url(image_bytes: bytes) -> str:
|
||||
encoded = base64.b64encode(image_bytes).decode("ascii")
|
||||
return f"data:image/png;base64,{encoded}"
|
||||
|
||||
|
||||
def _parse_coordinates(text: str) -> tuple[int, int]:
|
||||
numbers = re.findall(r"-?\d+(?:\.\d+)?", text or "")
|
||||
if len(numbers) < 2:
|
||||
raise ValueError(f"grounding model did not return coordinates: {text!r}")
|
||||
x = int(float(numbers[0]))
|
||||
y = int(float(numbers[1]))
|
||||
x = max(0, min(GROUNDING_WIDTH - 1, x))
|
||||
y = max(0, min(GROUNDING_HEIGHT - 1, y))
|
||||
return x, y
|
||||
|
||||
|
||||
def _scale_to_screen(coords: tuple[int, int], screen_size: tuple[int, int]) -> tuple[int, int]:
|
||||
screen_width, screen_height = screen_size
|
||||
x, y = coords
|
||||
return (
|
||||
round(x * screen_width / GROUNDING_WIDTH),
|
||||
round(y * screen_height / GROUNDING_HEIGHT),
|
||||
)
|
||||
|
||||
|
||||
def _ground_element(element_description: str) -> tuple[int, int, str]:
|
||||
if not element_description or not element_description.strip():
|
||||
raise ValueError("element_description is required for this GUI action.")
|
||||
|
||||
raw_png, screen_size = _screenshot_png()
|
||||
grounding_png = _resize_for_grounding(raw_png)
|
||||
prompt = (
|
||||
"You are a visual grounding model for desktop GUI control.\n"
|
||||
f"The screenshot has been resized to exactly {GROUNDING_WIDTH}x{GROUNDING_HEIGHT} pixels.\n"
|
||||
f"Query: {element_description.strip()}\n"
|
||||
"Return only one target point as pixel coordinates in this exact format: x,y\n"
|
||||
f"Example: {GROUNDING_WIDTH // 2},{GROUNDING_HEIGHT // 2}\n"
|
||||
"Do not include any explanation."
|
||||
)
|
||||
response = llm.invoke(
|
||||
[
|
||||
HumanMessage(
|
||||
content=[
|
||||
{"type": "image_url", "image_url": {"url": _image_data_url(grounding_png)}},
|
||||
{"type": "text", "text": prompt},
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
response_text = str(response.content)
|
||||
gx, gy = _parse_coordinates(response_text)
|
||||
x, y = _scale_to_screen((gx, gy), screen_size)
|
||||
return x, y, f"grounded ({gx},{gy})"
|
||||
|
||||
|
||||
def _type_text(text: str) -> None:
|
||||
if not text:
|
||||
return
|
||||
has_unicode = any(ord(ch) > 127 for ch in text)
|
||||
if has_unicode:
|
||||
try:
|
||||
import pyperclip
|
||||
|
||||
pyperclip.copy(text)
|
||||
modifier = "command" if sys.platform == "darwin" else "ctrl"
|
||||
pyautogui.hotkey(modifier, "v")
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
pyautogui.write(text)
|
||||
|
||||
|
||||
def _select_all_and_clear() -> None:
|
||||
modifier = "command" if sys.platform == "darwin" else "ctrl"
|
||||
pyautogui.hotkey(modifier, "a")
|
||||
pyautogui.press("backspace")
|
||||
|
||||
|
||||
def _with_hold_keys_down(keys: list[str]) -> None:
|
||||
for key in keys or []:
|
||||
pyautogui.keyDown(key)
|
||||
|
||||
|
||||
def _with_hold_keys_up(keys: list[str]) -> None:
|
||||
for key in reversed(keys or []):
|
||||
pyautogui.keyUp(key)
|
||||
|
||||
|
||||
class GUITool(BaseTool):
|
||||
args_schema: type[BaseModel] = GUIToolInput
|
||||
name: str = "gui_tool"
|
||||
description: str = (
|
||||
"GUI automation tool. Visually locates desktop elements from a screenshot, maps coordinates to the "
|
||||
"real screen, and executes pyautogui actions. Supported actions: click, type, drag_and_drop, scroll, "
|
||||
"hotkey, hold_and_press, wait. If no action is given, returns a screenshot of the current screen."
|
||||
)
|
||||
response_format: Literal["content", "content_and_artifact"] = "content_and_artifact"
|
||||
|
||||
def _artifact_screenshot(self) -> dict:
|
||||
try:
|
||||
image_bytes, _ = _screenshot_png()
|
||||
return {"image_bytes": image_bytes}
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
def _run(
|
||||
self,
|
||||
action: Optional[str] = None, # ← 允许为 None
|
||||
element_description: Optional[str] = None,
|
||||
ending_description: Optional[str] = None,
|
||||
text: str = "",
|
||||
overwrite: bool = False,
|
||||
enter: bool = False,
|
||||
num_clicks: int = 1,
|
||||
button_type: str = "left",
|
||||
hold_keys: Optional[list[str]] = None,
|
||||
keys: Optional[list[str]] = None,
|
||||
press_keys: Optional[list[str]] = None,
|
||||
clicks: int = 0,
|
||||
shift: bool = False,
|
||||
wait_seconds: float = 0.0,
|
||||
) -> tuple[str, dict]:
|
||||
# ---------- 新增:无动作时直接截图 ----------
|
||||
if not action:
|
||||
raw_png, size = _screenshot_png()
|
||||
return (
|
||||
f"Screenshot captured (size {size[0]}x{size[1]}).",
|
||||
self._artifact_screenshot(), # 再次截图,间隔极短,也可直接返回 {"image_bytes": raw_png}
|
||||
)
|
||||
# -------------------------------------------
|
||||
try:
|
||||
message = self._dispatch(
|
||||
action=action,
|
||||
element_description=element_description,
|
||||
ending_description=ending_description,
|
||||
text=text,
|
||||
overwrite=overwrite,
|
||||
enter=enter,
|
||||
num_clicks=num_clicks,
|
||||
button_type=button_type,
|
||||
hold_keys=hold_keys or [],
|
||||
keys=keys or [],
|
||||
press_keys=press_keys or [],
|
||||
clicks=clicks,
|
||||
shift=shift,
|
||||
wait_seconds=wait_seconds,
|
||||
)
|
||||
time.sleep(0.2)
|
||||
return message, self._artifact_screenshot()
|
||||
except Exception as exc:
|
||||
return f"GUI tool action failed: {exc}", self._artifact_screenshot()
|
||||
|
||||
def _dispatch(self, **kwargs: Any) -> str:
|
||||
action = str(kwargs["action"]).strip()
|
||||
if action == "click":
|
||||
return self._click(**kwargs)
|
||||
if action == "type":
|
||||
return self._type(**kwargs)
|
||||
if action == "drag_and_drop":
|
||||
return self._drag_and_drop(**kwargs)
|
||||
if action == "scroll":
|
||||
return self._scroll(**kwargs)
|
||||
if action == "hotkey":
|
||||
return self._hotkey(**kwargs)
|
||||
if action == "hold_and_press":
|
||||
return self._hold_and_press(**kwargs)
|
||||
if action == "wait":
|
||||
seconds = min(1.0, float(kwargs["wait_seconds"]))
|
||||
time.sleep(seconds)
|
||||
return f"Waited {seconds:.2f} seconds."
|
||||
raise ValueError(f"unsupported GUI action: {action}")
|
||||
|
||||
def _click(self, **kwargs: Any) -> str:
|
||||
x, y, grounding = _ground_element(kwargs["element_description"] or "")
|
||||
hold_keys = kwargs["hold_keys"]
|
||||
_with_hold_keys_down(hold_keys)
|
||||
try:
|
||||
pyautogui.click(x, y, clicks=max(1, int(kwargs["num_clicks"])), button=kwargs["button_type"])
|
||||
finally:
|
||||
_with_hold_keys_up(hold_keys)
|
||||
return f"Clicked target. {grounding}"
|
||||
|
||||
def _type(self, **kwargs: Any) -> str:
|
||||
if kwargs["element_description"]:
|
||||
x, y, grounding = _ground_element(kwargs["element_description"])
|
||||
pyautogui.click(x, y)
|
||||
else:
|
||||
grounding = "typed into current focus"
|
||||
if kwargs["overwrite"]:
|
||||
_select_all_and_clear()
|
||||
_type_text(kwargs["text"])
|
||||
if kwargs["enter"]:
|
||||
pyautogui.press("enter")
|
||||
return f"Typed text. {grounding}"
|
||||
|
||||
def _drag_and_drop(self, **kwargs: Any) -> str:
|
||||
x1, y1, start_grounding = _ground_element(kwargs["element_description"] or "")
|
||||
x2, y2, end_grounding = _ground_element(kwargs["ending_description"] or "")
|
||||
hold_keys = kwargs["hold_keys"]
|
||||
pyautogui.moveTo(x1, y1)
|
||||
_with_hold_keys_down(hold_keys)
|
||||
try:
|
||||
pyautogui.dragTo(x2, y2, duration=1.0, button="left")
|
||||
pyautogui.mouseUp()
|
||||
finally:
|
||||
_with_hold_keys_up(hold_keys)
|
||||
return f"Dragged target. Start: {start_grounding}. End: {end_grounding}"
|
||||
|
||||
def _scroll(self, **kwargs: Any) -> str:
|
||||
x, y, grounding = _ground_element(kwargs["element_description"] or "")
|
||||
pyautogui.moveTo(x, y)
|
||||
time.sleep(0.1)
|
||||
clicks = int(kwargs["clicks"])
|
||||
if kwargs["shift"]:
|
||||
pyautogui.hscroll(clicks)
|
||||
else:
|
||||
pyautogui.vscroll(clicks)
|
||||
return f"Scrolled target by {clicks}. {grounding}"
|
||||
|
||||
def _hotkey(self, **kwargs: Any) -> str:
|
||||
keys = kwargs["keys"]
|
||||
if not keys:
|
||||
raise ValueError("keys is required for hotkey")
|
||||
pyautogui.hotkey(*keys)
|
||||
return f"Pressed hotkey {keys}."
|
||||
|
||||
def _hold_and_press(self, **kwargs: Any) -> str:
|
||||
hold_keys = kwargs["hold_keys"]
|
||||
press_keys = kwargs["press_keys"] or kwargs["keys"]
|
||||
if not press_keys:
|
||||
raise ValueError("press_keys or keys is required for hold_and_press")
|
||||
_with_hold_keys_down(hold_keys)
|
||||
try:
|
||||
pyautogui.press(press_keys)
|
||||
finally:
|
||||
_with_hold_keys_up(hold_keys)
|
||||
return f"Held {hold_keys} and pressed {press_keys}."
|
||||
|
||||
async def _arun(self, **kwargs: Any) -> tuple[str, dict]:
|
||||
return await asyncio.to_thread(self._run, **kwargs)
|
||||
+141
-18
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import os
|
||||
import io
|
||||
import json
|
||||
from typing import Optional, Literal, Tuple
|
||||
from typing import Any, Optional, Literal, Tuple
|
||||
import sys
|
||||
import time
|
||||
import webbrowser
|
||||
@@ -12,7 +12,7 @@ import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
from langchain.tools import BaseTool
|
||||
from pydantic import BaseModel, Field, create_model
|
||||
from pydantic import BaseModel, Field, create_model, model_validator
|
||||
|
||||
from agent.core.llm import llm
|
||||
from agent.utils.env_utils import BROWSER_MAP
|
||||
@@ -380,9 +380,94 @@ class HumanInteraction(BaseTool):
|
||||
return await asyncio.to_thread(self._run, interaction_type, title, prompt, options, suggested_response)
|
||||
|
||||
|
||||
# ---------- Todo List 实时推送共享存储 ----------
|
||||
# TodoList 工具本身和 web server 共享此 dict,无需跨模块 import server
|
||||
_todo_store: dict[str, dict] = {}
|
||||
|
||||
|
||||
def get_todo_store_data(session_id: str) -> dict | None:
|
||||
return _todo_store.get(session_id)
|
||||
|
||||
|
||||
def update_todo_store_session(
|
||||
session_id: str,
|
||||
steps: list[str] | None = None,
|
||||
done_step: int = -1,
|
||||
) -> dict | None:
|
||||
if not session_id:
|
||||
return None
|
||||
current = _todo_store.get(session_id) or {"steps": [], "done_steps": []}
|
||||
stored_steps = list(current.get("steps") or [])
|
||||
done_steps = list(current.get("done_steps") or [])
|
||||
|
||||
if steps:
|
||||
stored_steps = list(steps)
|
||||
done_steps = []
|
||||
|
||||
if stored_steps and 1 <= done_step <= len(stored_steps):
|
||||
idx = done_step - 1
|
||||
if idx not in done_steps:
|
||||
done_steps.append(idx)
|
||||
done_steps.sort()
|
||||
|
||||
todo_data = {"steps": stored_steps, "done_steps": done_steps}
|
||||
_todo_store[session_id] = todo_data
|
||||
return todo_data
|
||||
|
||||
|
||||
def update_todo_store_from_tool_args(session_id: str, tool_args: dict[str, Any]) -> dict | None:
|
||||
raw_steps = tool_args.get("steps") if isinstance(tool_args, dict) else None
|
||||
if isinstance(raw_steps, str):
|
||||
try:
|
||||
raw_steps = json.loads(raw_steps)
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
raw_steps = None
|
||||
steps = raw_steps if isinstance(raw_steps, list) else None
|
||||
|
||||
done_step = tool_args.get("done_step", -1) if isinstance(tool_args, dict) else -1
|
||||
try:
|
||||
done_step = int(done_step)
|
||||
except (TypeError, ValueError):
|
||||
done_step = -1
|
||||
|
||||
return update_todo_store_session(session_id, steps, done_step)
|
||||
|
||||
|
||||
def set_todo_store_data(session_id: str, todo_data: dict) -> None:
|
||||
if not session_id:
|
||||
return
|
||||
_todo_store[session_id] = {
|
||||
"steps": list(todo_data.get("steps") or []),
|
||||
"done_steps": list(todo_data.get("done_steps") or []),
|
||||
}
|
||||
|
||||
|
||||
def clear_todo_store_session(session_id: str) -> None:
|
||||
_todo_store.pop(session_id, None)
|
||||
|
||||
|
||||
class TodoListInput(BaseModel):
|
||||
steps: list[str] = Field(default_factory=list, description="任务步骤列表,每项为一步要做什么的描述")
|
||||
step: int = Field(default=-1, description="已完成的步骤索引(从0开始),-1表示无已完成步骤")
|
||||
steps: Optional[list[str]] = Field(
|
||||
default=None,
|
||||
description="任务步骤列表,每项为一步的描述。仅在首次创建计划时传入,后续更新进度时请省略此参数以节省 token。",
|
||||
)
|
||||
done_step: int = Field(
|
||||
default=-1,
|
||||
description="本次新完成的步骤编号(从1开始)。-1 表示仅查看当前状态,不标记完成。",
|
||||
)
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def parse_steps_string(cls, data: Any) -> Any:
|
||||
"""容错:如果 steps 以 JSON 字符串形式传入(某些 LLM 的 function calling 行为),自动解析为列表。"""
|
||||
if isinstance(data, dict) and isinstance(data.get("steps"), str):
|
||||
raw = data["steps"].strip()
|
||||
if raw.startswith("["):
|
||||
try:
|
||||
data["steps"] = json.loads(raw)
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
pass
|
||||
return data
|
||||
|
||||
|
||||
class TodoList(BaseTool):
|
||||
@@ -391,23 +476,61 @@ class TodoList(BaseTool):
|
||||
args_schema: type[BaseModel] = TodoListInput
|
||||
name: str = "todo_list"
|
||||
description: str = (
|
||||
"可选的任务管理工具。当你需要拆分复杂任务为多个步骤并跟踪进度时调用。"
|
||||
"输入 steps (list[str]) 为每一步的描述,step (int) 为已完成的步骤索引(从0开始)。"
|
||||
"任务默认状态为 pending,根据 step 自动标记完成。工具返回当前 todo list 文本。"
|
||||
"可选的任务管理工具。当你需要拆分复杂任务为多个步骤并跟踪进度时调用。\n"
|
||||
"参数说明:\n"
|
||||
"- steps (list[str], 可选):任务步骤列表。仅在首次创建计划时传入所有步骤。后续更新进度时请省略此参数(传 null 或不传),工具会复用之前设定的步骤列表,省去重复传输。\n"
|
||||
"- done_step (int, 可选):刚完成的步骤编号(从1开始),一次只标记一个。不传或传 -1 则只查看当前状态不做修改。\n"
|
||||
"调用示例:\n"
|
||||
'- 首次创建:{"steps": ["准备食材", "切菜", "烹饪", "装盘"], "done_step": -1}\n'
|
||||
'- 完成第1步:"done_step": 1}(省略 steps)\n'
|
||||
'- 查看状态:不传任何参数或 {"done_step": -1}\n'
|
||||
"仅在需要跟踪多步骤任务进度时调用,简单任务不需要调用。"
|
||||
)
|
||||
response_format: Literal["content", "content_and_artifact"] = "content"
|
||||
|
||||
def _run(self, steps: list[str] | None = None, step: int = -1) -> str:
|
||||
def _run(self, steps: list[str] | None = None, done_step: int = -1) -> str:
|
||||
steps = steps or []
|
||||
step = max(-1, min(step, len(steps) - 1))
|
||||
lines = ["## 📋 任务列表"]
|
||||
for i, s in enumerate(steps):
|
||||
status = "✅ 已完成" if i <= step else "⏳ 待处理"
|
||||
lines.append(f"- [{i+1}/{len(steps)}] {status}: {s}")
|
||||
if not steps:
|
||||
lines.append("(无任务)")
|
||||
return "\n".join(lines)
|
||||
|
||||
async def _arun(self, steps: list[str] | None = None, step: int = -1) -> str:
|
||||
return self._run(steps, step)
|
||||
# 用简单变量维护状态(同一实例在 AgentRuntime 生命周期内复用)
|
||||
if not hasattr(self, "_stored_steps"):
|
||||
self._stored_steps: list[str] = []
|
||||
if not hasattr(self, "_done_set"):
|
||||
self._done_set: list[int] = []
|
||||
|
||||
# 如果传入了新的 steps,更新存储(可能是新任务替换旧任务)
|
||||
if steps:
|
||||
self._stored_steps = steps
|
||||
self._done_set = []
|
||||
|
||||
# 标记新完成步骤(1-indexed → 0-indexed)
|
||||
if self._stored_steps and 1 <= done_step <= len(self._stored_steps):
|
||||
idx = done_step - 1
|
||||
if idx not in self._done_set:
|
||||
self._done_set.append(idx)
|
||||
self._done_set.sort()
|
||||
|
||||
todo_data = {
|
||||
"steps": self._stored_steps,
|
||||
"done_steps": list(self._done_set),
|
||||
}
|
||||
|
||||
# 实时推送到前端 todo 浮窗(共享 store,无需 import server)
|
||||
try:
|
||||
from agent.history.tool_call_recorder import get_current_session
|
||||
sid = get_current_session()
|
||||
if sid:
|
||||
set_todo_store_data(sid, todo_data)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not self._stored_steps:
|
||||
return json.dumps({"__todo_list__": True, "data": todo_data}, ensure_ascii=False) + "\n[Todo List]\n(当前无任务)"
|
||||
|
||||
lines = ["[Todo List]"]
|
||||
for i, s in enumerate(self._stored_steps):
|
||||
status = "[Done]" if i in self._done_set else "[Pending]"
|
||||
lines.append(f"- [{i+1}/{len(self._stored_steps)}] {status}: {s}")
|
||||
return json.dumps({"__todo_list__": True, "data": todo_data}, ensure_ascii=False) + "\n" + "\n".join(lines)
|
||||
|
||||
async def _arun(self, steps: list[str] | None = None, done_step: int = -1) -> str:
|
||||
return self._run(steps, done_step)
|
||||
@@ -1,118 +0,0 @@
|
||||
from langchain.tools import BaseTool
|
||||
from pydantic import Field, create_model, BaseModel
|
||||
from utils.database_utils import PostgreSQL_Manager
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
class ListTableComment(BaseTool):
|
||||
|
||||
name: str = "list_table_comment"
|
||||
description: str = "一个列出数据库中所有表名和对应描述的工具,可以根据用户的查询返回相关的表名和描述列表。"
|
||||
db_manager : PostgreSQL_Manager
|
||||
|
||||
def _run(self, query: str) -> str:
|
||||
# 这里可以实现具体的列出数据库表的逻辑,例如调用数据库管理类的方法
|
||||
# 下面是一个示例实现,实际使用时请替换为你自己的数据库查询逻辑
|
||||
try:
|
||||
result_json = self.db_manager.get_table_comments()
|
||||
results = json.loads(result_json)
|
||||
if not isinstance(results, dict):
|
||||
raise ValueError("get_table_comments did not return a JSON object")
|
||||
result = f"数据库中共有 {len(results)} 张表"
|
||||
for i, (table_name, comment) in enumerate(results.items()):
|
||||
if not comment or comment.isspace():
|
||||
comment = "(暂无描述)"
|
||||
result += f"\n{i+1}. {table_name}: {comment}"
|
||||
return result
|
||||
except Exception as e:
|
||||
return f"执行列出数据库表和描述时出错: {str(e)}"
|
||||
|
||||
async def _arun(self, query: str) -> str:
|
||||
return self._run(query)
|
||||
|
||||
class ListTableSchema(BaseTool):
|
||||
|
||||
name: str = "list_table_schema"
|
||||
description: str = "一个列出数据库中表名对应模式的工具,可以根据用户的查询返回相关的表名和模式信息,包括列定义、主键、外键等。输入应为逗号分隔的表名列表。"
|
||||
db_manager: PostgreSQL_Manager
|
||||
args_schema: type[BaseModel] = create_model("Input_type", table_names=(Optional[str], Field(description="要查询的表名列表,如果为None则返回所有表的模式信息,例如:'table1', 'table2'")))
|
||||
|
||||
def _run(self, table_names : Optional[str] = None) -> str:
|
||||
try:
|
||||
if table_names:
|
||||
table_list = [name.strip() for name in table_names.split(",")]
|
||||
result_json = self.db_manager.get_table_schema(table_list)
|
||||
results = json.loads(result_json)
|
||||
if not isinstance(results, dict):
|
||||
raise ValueError("get_table_schema did not return a JSON object")
|
||||
return json.dumps(results, ensure_ascii=False, indent=2)
|
||||
except Exception as e:
|
||||
return f"执行列出数据库表和模式时出错: {str(e)}"
|
||||
|
||||
async def _arun(self, table_names : Optional[str] = None) -> str:
|
||||
return self._run(table_names)
|
||||
|
||||
class SQLQuery(BaseTool):
|
||||
|
||||
name: str = "sql_query"
|
||||
description: str = "在PostgreSQL数据库中执行安全的SELECT或WITH查询并返回结果。"
|
||||
db_manager: PostgreSQL_Manager
|
||||
args_schema: type[BaseModel] = create_model("Input_type", query=(str, Field(description="要执行的SQL SELECT查询语句")))
|
||||
|
||||
def _run(self, query: str) -> str:
|
||||
try:
|
||||
result_json = self.db_manager.execute_query(query)
|
||||
results = json.loads(result_json)
|
||||
if isinstance(results, list):
|
||||
# 成功返回数据行
|
||||
return json.dumps(results, ensure_ascii=False, indent=2)
|
||||
elif isinstance(results, dict):
|
||||
if "error" in results:
|
||||
# 返回错误信息
|
||||
return f"查询执行失败: {results['error']}"
|
||||
elif "message" in results:
|
||||
# 查询成功但无数据返回
|
||||
return results["message"]
|
||||
else:
|
||||
# 其他字典格式的结果
|
||||
return json.dumps(results, ensure_ascii=False, indent=2)
|
||||
else:
|
||||
raise ValueError(f"execute_query 返回了意外的数据类型: {type(results)}")
|
||||
except Exception as e:
|
||||
return f"执行SQL查询时出错: {str(e)}"
|
||||
|
||||
async def _arun(self, query: str) -> str:
|
||||
return self._run(query)
|
||||
|
||||
class SQLQueryChecker(BaseTool):
|
||||
|
||||
name: str = "sql_query_checker"
|
||||
description: str = "一个检查SQL查询语句是否安全的工具,主要用于防止SQL注入攻击。输入为SQL查询语句,输出为检查结果。"
|
||||
args_schema: type[BaseModel] = create_model("Input_type", query=(str, Field(description="要检查的SQL查询语句")))
|
||||
db_manager: PostgreSQL_Manager
|
||||
|
||||
def _run(self, query: str) -> str:
|
||||
try:
|
||||
result_json = self.db_manager.validate_query(query)
|
||||
result = json.loads(result_json)
|
||||
if not isinstance(result, dict):
|
||||
raise ValueError("validate_query did not return a JSON object")
|
||||
return json.dumps(result, ensure_ascii=False, indent=2)
|
||||
except Exception as e:
|
||||
return f"执行SQL查询安全检查时出错: {str(e)}"
|
||||
async def _arun(self, query: str) -> str:
|
||||
return self._run(query)
|
||||
|
||||
if __name__ == "__main__":
|
||||
manager = PostgreSQL_Manager("postgresql+psycopg2://postgres:12345678@localhost:5432/agent_controller")
|
||||
list_table_tool = ListTableComment(db_manager=manager)
|
||||
print(list_table_tool.run("列出数据库中的所有表和描述"))
|
||||
# list_table_tool = ListTableSchema(db_manager=manager)
|
||||
# print(list_table_tool.run({"table_names": []}))
|
||||
# sql_query = SQLQuery(db_manager=manager)
|
||||
# print(sql_query.run("SELECT * FROM tasks;"))
|
||||
# sql_query_checker = SQLQueryChecker(db_manager=manager)
|
||||
# print("Valid query:")
|
||||
# print(sql_query_checker.run("SELECT * FROM tasks;"))
|
||||
# print("\nInvalid query:")
|
||||
# print(sql_query_checker.run("DROP TABLE tasks;"))
|
||||
@@ -404,7 +404,6 @@ class SoftwareControl(BaseTool):
|
||||
action=(str, Field(default="open", description="操作类型:open 表示打开软件,close 表示正常关闭软件,force_close 表示强制关闭软件。")),
|
||||
software_name=(str, Field(description="要操作的软件名称,例如微信、Visual Studio Code、MATLAB。")),
|
||||
rescan=(bool, Field(default=False, description="是否强制重新扫描本机软件并刷新索引。")),
|
||||
force=(bool, Field(default=False, description="兼容旧参数。当前仅当 action=force_close 时才会真正触发强制关闭。")),
|
||||
)
|
||||
name: str = "software_control"
|
||||
description: str = (
|
||||
@@ -413,7 +412,7 @@ class SoftwareControl(BaseTool):
|
||||
"action=force_close 时才允许在正常关闭失败后强制结束进程。仅当用户明确要求打开、关闭或强制关闭某个本机软件时调用。"
|
||||
)
|
||||
|
||||
def _run(self, action: str = "open", software_name: str = "", rescan: bool = False, force: bool = False) -> str:
|
||||
def _run(self, action: str = "open", software_name: str = "", rescan: bool = False) -> str:
|
||||
if os.name != "nt":
|
||||
return "software_control 目前仅支持 Windows 本机软件操作。"
|
||||
query = (software_name or "").strip()
|
||||
@@ -421,11 +420,8 @@ class SoftwareControl(BaseTool):
|
||||
return "软件名称不能为空。"
|
||||
action = (action or "open").strip().lower()
|
||||
rescan = _coerce_bool(rescan)
|
||||
force = _coerce_bool(force)
|
||||
if action not in {"open", "close", "force_close"}:
|
||||
return "action 仅支持 open、close 或 force_close。"
|
||||
if force and action == "close":
|
||||
action = "force_close"
|
||||
force_requested = action == "force_close"
|
||||
|
||||
records = _build_index() if rescan else _load_index()
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -86,8 +86,7 @@ def parse_edited_instruction(instruction: str, fallback: dict[str, Any]) -> dict
|
||||
"""解析用户在「编辑」确认流程中提交的指令文本。
|
||||
|
||||
功能描述:
|
||||
优先按 JSON 解析完整工具调用;失败则将原文本写入 ``args.instruction``;
|
||||
兼容 ``tool_name`` 别名字段。
|
||||
优先按 JSON 解析完整工具调用;失败则将原文本写入 ``args.instruction``。
|
||||
|
||||
输入:
|
||||
instruction: 用户编辑后的文本(可为 JSON 或纯文本)。
|
||||
@@ -113,8 +112,6 @@ def parse_edited_instruction(instruction: str, fallback: dict[str, Any]) -> dict
|
||||
updated["args"] = {"instruction": raw}
|
||||
return updated
|
||||
if isinstance(parsed, dict):
|
||||
if "name" not in parsed and "tool_name" in parsed:
|
||||
parsed["name"] = parsed.get("tool_name")
|
||||
if "args" not in parsed:
|
||||
parsed["args"] = fallback.get("args") or {}
|
||||
return parsed
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
# Plan TodoList
|
||||
|
||||
路径选择:路径一:使用浏览器控制
|
||||
|
||||
# 执行计划:读取 Bilibili 首页第一个视频标题并分析
|
||||
|
||||
**说明**:本计划旨在通过浏览器自动化控制访问 Bilibili 首页,提取首个视频标题,并提供基于当前网络热点和一般认知的理解。由于我无法直接“看到”实时变化的互联网内容,以下流程将指导如何获取最新数据并进行分析。在实际执行中,我会根据获取到的具体标题进行针对性解读。
|
||||
|
||||
## Todo List
|
||||
|
||||
- [x] **步骤 1:环境准备与导航** · 状态:进行中 · 状态:已完成
|
||||
- **依赖**:无
|
||||
- **状态**:待开始
|
||||
- **任务描述**:
|
||||
1. 启动内置浏览器或模拟浏览器会话。
|
||||
2. 导航至 Bilibili 首页 URL (`https://www.bilibili.com`)。
|
||||
3. 等待页面完全加载,确保动态内容(如推荐流)已渲染完毕。
|
||||
|
||||
- [x] **步骤 2:定位首个视频元素** · 状态:进行中 · 状态:已完成
|
||||
- **依赖**:步骤 1 完成
|
||||
- **状态**:待开始
|
||||
- **任务描述**:
|
||||
1. 识别首页的主要视频推荐区域(通常是“推荐”标签页下的第一个卡片)。
|
||||
2. 定位包含视频标题的 DOM 元素(通常位于视频封面下方或悬浮层中)。
|
||||
3. *注意*:需排除广告位或非视频内容(如直播入口、专栏文章),确保选取的是标准的 UP 主投稿视频。
|
||||
|
||||
- [x] **步骤 3:提取视频标题文本** · 状态:进行中 · 状态:已完成
|
||||
- **依赖**:步骤 2 完成
|
||||
- **状态**:待开始
|
||||
- **任务描述**:
|
||||
1. 从定位到的元素中提取纯文本标题。
|
||||
2. 清理多余的空白字符或隐藏的控制符。
|
||||
3. 记录提取结果,若提取失败则重试或尝试备用选择器。
|
||||
|
||||
- [x] **步骤 4:信息分析与理解生成** · 状态:进行中 · 状态:已完成
|
||||
- **依赖**:步骤 3 完成
|
||||
- **状态**:待开始
|
||||
- **任务描述**:
|
||||
1. 基于提取的标题,结合当前的社会热点、B站社区文化及常见视频类型进行分析。
|
||||
2. 判断视频可能的主题(如科技评测、生活 vlog、游戏实况、知识科普等)。
|
||||
3. 评估标题是否具有吸引力(如是否使用悬念、热点词汇、情绪化表达)。
|
||||
4. 给出简短的个性化理解和建议观看理由。
|
||||
|
||||
- [x] **步骤 5:输出最终报告** · 状态:进行中 · 状态:已完成
|
||||
- **依赖**:步骤 4 完成
|
||||
- **状态**:待开始
|
||||
- **任务描述**:
|
||||
1. 整合提取的标题和分析结果。
|
||||
2. 以清晰的结构呈现给用户,包括“原始标题”、“内容推测”和“我的理解”。
|
||||
|
||||
---
|
||||
|
||||
**备注**:在执行过程中,我将根据实际抓取到的标题实时更新后续的分析内容。如果首页因登录状态或算法推荐显示不同内容,我将基于当时可见的第一个视频进行处理。
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+137
-523
@@ -487,85 +487,6 @@ body {
|
||||
background: var(--bubble-assistant);
|
||||
}
|
||||
|
||||
.pending-action-card {
|
||||
margin-top: 0.65rem;
|
||||
padding: 0.7rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(14, 116, 144, 0.22);
|
||||
background: rgba(236, 254, 255, 0.28);
|
||||
color: rgba(51, 65, 85, 0.82);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.pending-action-card--resolved {
|
||||
opacity: 0.58;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pending-action-title {
|
||||
font-weight: 700;
|
||||
color: rgba(15, 23, 42, 0.72);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.pending-action-params {
|
||||
margin: 0 0 0.45rem;
|
||||
padding: 0.5rem;
|
||||
max-height: 160px;
|
||||
overflow: auto;
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 0.32);
|
||||
color: rgba(30, 41, 59, 0.72);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
font-size: 0.72rem;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.pending-action-editor {
|
||||
width: 100%;
|
||||
resize: vertical;
|
||||
border: 1px solid rgba(148, 163, 184, 0.42);
|
||||
border-radius: 10px;
|
||||
padding: 0.45rem 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.36);
|
||||
color: rgba(15, 23, 42, 0.78);
|
||||
font: inherit;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.pending-action-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.pending-action-btn {
|
||||
border: 1px solid rgba(148, 163, 184, 0.45);
|
||||
border-radius: 999px;
|
||||
padding: 0.32rem 0.62rem;
|
||||
background: rgba(255, 255, 255, 0.42);
|
||||
color: #334155;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.pending-action-btn:hover:not(:disabled) {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.pending-action-btn--approve {
|
||||
border-color: rgba(20, 184, 166, 0.45);
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.pending-action-btn--reject {
|
||||
border-color: rgba(248, 113, 113, 0.45);
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
/* 图片在气泡内留出呼吸空间 */
|
||||
.msg-bubble img {
|
||||
display: block;
|
||||
@@ -2193,18 +2114,7 @@ body {
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ===== 图谱切换按钮 ===== */
|
||||
.graph-toggle-btn {
|
||||
margin-left: 0.15rem;
|
||||
}
|
||||
|
||||
.graph-toggle-btn.graph-active {
|
||||
background: rgba(14, 116, 144, 0.18);
|
||||
border-color: rgba(14, 116, 144, 0.5);
|
||||
color: #0e7490;
|
||||
}
|
||||
|
||||
/* ===== 主内容区(chat + graph 并排) ===== */
|
||||
/* ===== 主内容区 ===== */
|
||||
.main-content-wrap {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
@@ -2213,329 +2123,6 @@ body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 图谱开启时聊天面板缩窄,圆角只保留左侧 */
|
||||
.main.has-graph .chat-shell {
|
||||
flex: 1 1 58%;
|
||||
min-width: 320px;
|
||||
border-radius: var(--radius) 0 0 var(--radius);
|
||||
}
|
||||
|
||||
/* ===== 图谱侧面板 ===== */
|
||||
.graph-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 42%;
|
||||
min-width: 340px;
|
||||
max-width: 520px;
|
||||
border-left: 1px solid rgba(148, 163, 184, 0.35);
|
||||
border-radius: 0 var(--radius) var(--radius) 0;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
overflow: hidden;
|
||||
box-shadow: -4px 0 24px rgba(15, 23, 42, 0.06);
|
||||
transition: width 0.3s ease, min-width 0.3s ease, max-width 0.3s ease, opacity 0.25s ease;
|
||||
}
|
||||
|
||||
.graph-panel[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.graph-toolbar {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.45rem 0.65rem;
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.22);
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.graph-toolbar-title {
|
||||
font-size: 0.82rem;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.graph-btn {
|
||||
font-size: 0.7rem;
|
||||
padding: 0.2rem 0.45rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.graph-legend {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.65rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.graph-close-btn {
|
||||
width: 1.65rem;
|
||||
height: 1.65rem;
|
||||
padding: 0;
|
||||
border: 1px solid rgba(148, 163, 184, 0.4);
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
color: #64748b;
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.graph-close-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.65);
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.legend-dot {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 0.1rem;
|
||||
}
|
||||
|
||||
.legend-running {
|
||||
background: #f59e0b;
|
||||
box-shadow: 0 0 6px rgba(245, 158, 11, 0.5);
|
||||
animation: legendPulse 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.legend-done {
|
||||
background: #22c55e;
|
||||
}
|
||||
|
||||
.legend-error {
|
||||
background: #ef4444;
|
||||
}
|
||||
|
||||
.legend-idle {
|
||||
background: #94a3b8;
|
||||
}
|
||||
|
||||
@keyframes legendPulse {
|
||||
0%, 100% { opacity: 0.7; box-shadow: 0 0 4px rgba(245, 158, 11, 0.3); }
|
||||
50% { opacity: 1; box-shadow: 0 0 10px rgba(245, 158, 11, 0.7); }
|
||||
}
|
||||
|
||||
/* 图谱画布 */
|
||||
.graph-canvas-wrap {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
cursor: grab;
|
||||
background:
|
||||
radial-gradient(circle, rgba(148, 163, 184, 0.06) 1px, transparent 1px);
|
||||
background-size: 24px 24px;
|
||||
}
|
||||
|
||||
.graph-canvas-wrap:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.graph-canvas-wrap.panning {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.graph-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Agent 节点 */
|
||||
.graph-node {
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.graph-node:hover .graph-node-rect {
|
||||
filter: brightness(1.05);
|
||||
}
|
||||
|
||||
.graph-node-rect {
|
||||
fill: rgba(255, 255, 255, 0.75);
|
||||
stroke: rgba(148, 163, 184, 0.5);
|
||||
stroke-width: 1.5;
|
||||
rx: 16;
|
||||
transition: fill 0.35s ease, stroke 0.35s ease, filter 0.35s ease;
|
||||
}
|
||||
|
||||
/* 状态颜色 */
|
||||
.graph-node.status-running .graph-node-rect {
|
||||
fill: rgba(255, 200, 120, 0.45);
|
||||
stroke: #f59e0b;
|
||||
stroke-width: 2.5;
|
||||
filter: drop-shadow(0 0 8px rgba(245, 158, 11, 0.35));
|
||||
}
|
||||
|
||||
.graph-node.status-done .graph-node-rect {
|
||||
fill: rgba(187, 247, 208, 0.45);
|
||||
stroke: #22c55e;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.graph-node.status-error .graph-node-rect {
|
||||
fill: rgba(254, 202, 202, 0.5);
|
||||
stroke: #ef4444;
|
||||
stroke-width: 2.5;
|
||||
filter: drop-shadow(0 0 6px rgba(239, 68, 68, 0.3));
|
||||
}
|
||||
|
||||
.graph-node.status-idle .graph-node-rect {
|
||||
fill: rgba(255, 255, 255, 0.5);
|
||||
stroke: rgba(148, 163, 184, 0.4);
|
||||
}
|
||||
|
||||
/* Agent 节点内部文字 */
|
||||
.graph-node-icon {
|
||||
font-size: 1.4rem;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: central;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.graph-node-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
fill: #1e293b;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: central;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.graph-node-role {
|
||||
font-size: 0.6rem;
|
||||
fill: #94a3b8;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: central;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.graph-node-badge {
|
||||
font-size: 0.6rem;
|
||||
font-weight: 700;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: central;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 连线 */
|
||||
.graph-edge {
|
||||
transition: stroke 0.4s ease, stroke-width 0.4s ease, opacity 0.4s ease;
|
||||
}
|
||||
|
||||
.graph-edge.highlighted {
|
||||
stroke: #f59e0b;
|
||||
stroke-width: 2.5;
|
||||
opacity: 1;
|
||||
stroke-dasharray: none;
|
||||
}
|
||||
|
||||
.graph-edge.active {
|
||||
stroke: #14b8a6;
|
||||
stroke-width: 2;
|
||||
opacity: 1;
|
||||
stroke-dasharray: 6 3;
|
||||
animation: edgeFlow 0.6s linear infinite;
|
||||
}
|
||||
|
||||
.graph-edge.normal {
|
||||
stroke: rgba(148, 163, 184, 0.4);
|
||||
stroke-width: 1.5;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
@keyframes edgeFlow {
|
||||
from { stroke-dashoffset: 18; }
|
||||
to { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
/* 工具调用面板 */
|
||||
/* 工具调用卡片 */
|
||||
.toolcall-card {
|
||||
padding: 0.4rem 0.55rem;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.3);
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.toolcall-card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
.toolcall-card-name {
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
.toolcall-card-status {
|
||||
font-size: 0.62rem;
|
||||
padding: 0.1rem 0.35rem;
|
||||
border-radius: 999px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.toolcall-card-status.success {
|
||||
background: rgba(34, 197, 94, 0.15);
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
.toolcall-card-status.error {
|
||||
background: rgba(239, 68, 68, 0.15);
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.toolcall-card-status.pending {
|
||||
background: rgba(245, 158, 11, 0.15);
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.toolcall-card-args {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
font-size: 0.65rem;
|
||||
color: #64748b;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.toolcall-card-result {
|
||||
margin-top: 0.25rem;
|
||||
padding: 0.3rem 0.4rem;
|
||||
border-radius: 6px;
|
||||
background: rgba(241, 245, 249, 0.7);
|
||||
font-size: 0.65rem;
|
||||
color: #475569;
|
||||
max-height: 48px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* 消息传递动画脉冲 */
|
||||
@keyframes msgPulse {
|
||||
0%, 100% { opacity: 0.4; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
.graph-edge.active {
|
||||
animation: edgeFlow 0.6s linear infinite, msgPulse 0.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* ===== 工具调用气泡(chat 视图) ===== */
|
||||
.tool-call-bubble-wrap {
|
||||
margin-top: 0.5rem;
|
||||
@@ -2617,6 +2204,15 @@ body {
|
||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||
}
|
||||
|
||||
.tool-call-detail--running {
|
||||
border-color: rgba(245, 158, 11, 0.4);
|
||||
background: rgba(245, 158, 11, 0.05);
|
||||
}
|
||||
|
||||
.tool-call-detail--running .tool-call-detail-name {
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.tool-call-detail-name {
|
||||
font-weight: 700;
|
||||
font-size: 0.72rem;
|
||||
@@ -2643,37 +2239,43 @@ body {
|
||||
background: rgba(148, 163, 184, 0.08);
|
||||
}
|
||||
|
||||
.tool-call-detail-img {
|
||||
border-radius: 6px;
|
||||
margin: 0.2rem;
|
||||
max-width: 120px;
|
||||
max-height: 90px;
|
||||
object-fit: cover;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.3s;
|
||||
.tool-call-detail-media {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.45rem;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
.tool-call-detail-img:hover {
|
||||
opacity: 0.9;
|
||||
.tool-call-detail .tool-call-detail-image {
|
||||
display: block;
|
||||
max-width: min(220px, 100%);
|
||||
max-height: 180px;
|
||||
object-fit: contain;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||
background: rgba(255, 255, 255, 0.45);
|
||||
cursor: zoom-in;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 淡出动画 */
|
||||
.tool-call-block--fading {
|
||||
transition: opacity 0.8s ease-out;
|
||||
opacity: 0 !important;
|
||||
pointer-events: none;
|
||||
.tool-call-detail .tool-call-detail-image:hover {
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
/* ===== 实时工具调用(thinking 气泡内) ===== */
|
||||
.tool-call-live-wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.3rem;
|
||||
margin-top: 0.5rem;
|
||||
padding: 0.4rem 0.5rem;
|
||||
display: block;
|
||||
width: min(560px, 100%);
|
||||
margin-top: 0.6rem;
|
||||
padding-top: 0.55rem;
|
||||
border-top: 1px solid rgba(148, 163, 184, 0.2);
|
||||
}
|
||||
|
||||
.tool-call-live-wrap .tool-call-bubble-wrap {
|
||||
margin-top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tool-call-live-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -2699,105 +2301,117 @@ body {
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
/* 任务完成提示 */
|
||||
.tool-call-done-badge {
|
||||
display: inline-block;
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 999px;
|
||||
background: rgba(34, 197, 94, 0.15);
|
||||
color: #15803d;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
/* ===== Graph 节点工具调用缩略 ===== */
|
||||
.graph-node-tool-chip {
|
||||
font-size: 0.55rem;
|
||||
font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
|
||||
fill: #6366f1;
|
||||
font-weight: 600;
|
||||
text-anchor: start;
|
||||
}
|
||||
|
||||
/* ===== Agent 详情弹窗 ===== */
|
||||
.agent-detail-overlay {
|
||||
.todo-list-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 999;
|
||||
background: rgba(15, 23, 42, 0.55);
|
||||
top: 80px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 900;
|
||||
min-width: 280px;
|
||||
max-width: 420px;
|
||||
width: max-content;
|
||||
background: rgba(255, 255, 255, 0.82);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
|
||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.todo-list-header {
|
||||
padding: 0.6rem 1rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
color: #334155;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.15);
|
||||
}
|
||||
|
||||
.todo-list-header:hover {
|
||||
background: rgba(99, 102, 241, 0.06);
|
||||
}
|
||||
|
||||
.todo-list-body {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0.5rem 0;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.todo-list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.4rem 1rem;
|
||||
font-size: 0.8rem;
|
||||
color: #475569;
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.08);
|
||||
}
|
||||
.todo-list-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.todo-label {
|
||||
flex: 1;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* ----- 勾已完成 ----- */
|
||||
.todo-check {
|
||||
flex-shrink: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: #22c55e;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.agent-detail-overlay[hidden] {
|
||||
/* ----- 加载旋转动画(pending) ----- */
|
||||
.todo-spinner {
|
||||
flex-shrink: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
border: 2.5px solid rgba(99, 102, 241, 0.2);
|
||||
border-top-color: #6366f1;
|
||||
animation: todo-spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes todo-spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* ----- 折叠态 ----- */
|
||||
.todo-list-overlay--collapsed .todo-list-body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.agent-detail-dialog {
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-radius: 16px;
|
||||
width: min(520px, 92vw);
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.2);
|
||||
.todo-list-overlay--collapsed {
|
||||
min-width: 180px;
|
||||
max-width: 240px;
|
||||
}
|
||||
|
||||
.agent-detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem 1.25rem;
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.25);
|
||||
}
|
||||
|
||||
.agent-detail-header h2 {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.agent-detail-close {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: #64748b;
|
||||
line-height: 1;
|
||||
padding: 0.2rem;
|
||||
}
|
||||
|
||||
.agent-detail-body {
|
||||
padding: 1rem 1.25rem;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.agent-detail-section {
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
|
||||
.agent-detail-section h3 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
color: #475569;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.agent-detail-section code {
|
||||
padding: 0.1rem 0.3rem;
|
||||
border-radius: 4px;
|
||||
background: rgba(79, 70, 229, 0.08);
|
||||
font-size: 0.75rem;
|
||||
color: #4f46e5;
|
||||
/* ===== 响应式 ===== */
|
||||
@media (max-width: 640px) {
|
||||
.todo-list-overlay {
|
||||
left: 1rem;
|
||||
right: 1rem;
|
||||
transform: none;
|
||||
min-width: unset;
|
||||
max-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+167
-548
@@ -1094,206 +1094,174 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 工具调用展示(chat 视图) =====
|
||||
let toolCallDataCache = { recent: [], detail: [], complete: false };
|
||||
let toolCallTimer = null;
|
||||
// ===== Todo List 悬浮窗 =====
|
||||
let _currentTodoOverlay = null;
|
||||
|
||||
async function fetchToolCallData() {
|
||||
if (!sessionId) return;
|
||||
try {
|
||||
const [recentRes, detailRes, pendingRes] = await Promise.all([
|
||||
api(`/api/tool-calls/${encodeURIComponent(sessionId)}/recent`),
|
||||
api(`/api/tool-calls/${encodeURIComponent(sessionId)}/detail`),
|
||||
api(`/api/tool-calls/${encodeURIComponent(sessionId)}/has-pending`),
|
||||
]);
|
||||
toolCallDataCache = {
|
||||
recent: recentRes.tool_calls || [],
|
||||
detail: detailRes.details || [],
|
||||
complete: pendingRes.is_complete,
|
||||
};
|
||||
} catch {
|
||||
/* ignore */
|
||||
function buildTodoListOverlay(todoData) {
|
||||
// 移除旧的 overlay
|
||||
if (_currentTodoOverlay && _currentTodoOverlay.parentNode) {
|
||||
_currentTodoOverlay.parentNode.removeChild(_currentTodoOverlay);
|
||||
}
|
||||
}
|
||||
|
||||
function startToolCallPolling() {
|
||||
fetchToolCallData();
|
||||
toolCallTimer = setInterval(fetchToolCallData, 2000);
|
||||
}
|
||||
|
||||
function stopToolCallPolling(keepDisplay = false) {
|
||||
if (toolCallTimer) {
|
||||
clearInterval(toolCallTimer);
|
||||
toolCallTimer = null;
|
||||
if (!todoData || !todoData.steps || !todoData.steps.length) {
|
||||
_currentTodoOverlay = null;
|
||||
return null;
|
||||
}
|
||||
if (!keepDisplay) {
|
||||
// 清除过期工具调用显示(渐变消失)
|
||||
document.querySelectorAll(".tool-call-block--fading").forEach((el) => {
|
||||
el.addEventListener("transitionend", () => el.remove());
|
||||
el.style.opacity = "0";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function renderToolCallBlock(call, index, total, isComplete) {
|
||||
const block = document.createElement("div");
|
||||
block.className = "tool-call-block" + (isComplete ? " tool-call-block--fading" : "");
|
||||
const overlay = document.createElement("div");
|
||||
overlay.className = "todo-list-overlay";
|
||||
|
||||
// 头部
|
||||
const header = document.createElement("div");
|
||||
header.className = "tool-call-header";
|
||||
const name = document.createElement("span");
|
||||
name.className = "tool-call-name";
|
||||
name.textContent = `🔧 ${escapeHtml(call.name || "")}`;
|
||||
const args = document.createElement("span");
|
||||
args.className = "tool-call-args";
|
||||
args.textContent = call.args_summary || "";
|
||||
const status = document.createElement("span");
|
||||
status.className = "tool-call-status";
|
||||
status.textContent = call.status === "done" ? "✓" : "…";
|
||||
header.appendChild(name);
|
||||
header.appendChild(args);
|
||||
header.appendChild(status);
|
||||
block.appendChild(header);
|
||||
header.className = "todo-list-header";
|
||||
header.textContent = "📋 Todo List";
|
||||
header.addEventListener("click", () => {
|
||||
overlay.classList.toggle("todo-list-overlay--collapsed");
|
||||
});
|
||||
overlay.appendChild(header);
|
||||
|
||||
return block;
|
||||
// 列表
|
||||
const list = document.createElement("ul");
|
||||
list.className = "todo-list-body";
|
||||
todoData.steps.forEach((s, i) => {
|
||||
const item = document.createElement("li");
|
||||
item.className = "todo-list-item";
|
||||
const statusIcon = todoData.done_steps && todoData.done_steps.includes(i)
|
||||
? '<span class="todo-check">✓</span>'
|
||||
: '<span class="todo-spinner"></span>';
|
||||
item.innerHTML = `${statusIcon}<span class="todo-label">${escapeHtml(s)}</span>`;
|
||||
list.appendChild(item);
|
||||
});
|
||||
overlay.appendChild(list);
|
||||
|
||||
_currentTodoOverlay = overlay;
|
||||
return overlay;
|
||||
}
|
||||
|
||||
function renderToolCallDetail(call) {
|
||||
const detail = document.createElement("div");
|
||||
detail.className = "tool-call-detail";
|
||||
function updateTodoOverlay(todoData) {
|
||||
const overlay = buildTodoListOverlay(todoData);
|
||||
// 找到 chat area 的容器,插入或移除 overlay
|
||||
const container = chatMessages.closest(".chat-area") || chatMessages.parentElement;
|
||||
const existing = container.querySelector(".todo-list-overlay");
|
||||
if (existing) existing.remove();
|
||||
if (overlay) container.appendChild(overlay);
|
||||
}
|
||||
|
||||
const name = document.createElement("div");
|
||||
name.className = "tool-call-detail-name";
|
||||
name.textContent = `🔧 ${escapeHtml(call.name || "unknown")}`;
|
||||
detail.appendChild(name);
|
||||
function clearTodoOverlay() {
|
||||
if (_currentTodoOverlay && _currentTodoOverlay.parentNode) {
|
||||
_currentTodoOverlay.parentNode.removeChild(_currentTodoOverlay);
|
||||
}
|
||||
_currentTodoOverlay = null;
|
||||
document.querySelectorAll(".todo-list-overlay").forEach((el) => el.remove());
|
||||
}
|
||||
|
||||
// ===== 工具调用展示(chat 视图) =====
|
||||
let liveToolCallExpanded = false;
|
||||
|
||||
function injectLiveToolCalls(typingEl, liveCalls) {
|
||||
if (!typingEl || !liveCalls.length) return;
|
||||
const bubble = typingEl.querySelector(".msg-bubble") || typingEl;
|
||||
let liveWrap = bubble.querySelector(".tool-call-live-wrap");
|
||||
if (!liveWrap) {
|
||||
liveWrap = document.createElement("div");
|
||||
liveWrap.className = "tool-call-live-wrap";
|
||||
bubble.appendChild(liveWrap);
|
||||
}
|
||||
|
||||
const total = liveCalls.length;
|
||||
let wrap = liveWrap.querySelector(".tool-call-bubble-wrap");
|
||||
const isNewPanel = !wrap;
|
||||
if (!wrap) {
|
||||
wrap = document.createElement("div");
|
||||
liveWrap.appendChild(wrap);
|
||||
}
|
||||
|
||||
if (isNewPanel) liveToolCallExpanded = true;
|
||||
liveToolCallExpanded = liveToolCallExpanded || wrap.classList.contains("tool-call-bubble-wrap--expanded");
|
||||
wrap.className = "tool-call-bubble-wrap" + (liveToolCallExpanded ? " tool-call-bubble-wrap--expanded" : "");
|
||||
wrap.innerHTML = "";
|
||||
|
||||
const runningCount = liveCalls.filter((call) => call.status === "running").length;
|
||||
const header = document.createElement("button");
|
||||
header.type = "button";
|
||||
header.className = "tool-call-toggle";
|
||||
header.textContent = `🔧 工具调用 (${total}${runningCount ? `,执行中 ${runningCount}` : ""}) ${liveToolCallExpanded ? "▾" : "▸"}`;
|
||||
header.addEventListener("click", () => {
|
||||
liveToolCallExpanded = !liveToolCallExpanded;
|
||||
wrap.classList.toggle("tool-call-bubble-wrap--expanded", liveToolCallExpanded);
|
||||
header.textContent = `🔧 工具调用 (${total}${runningCount ? `,执行中 ${runningCount}` : ""}) ${liveToolCallExpanded ? "▾" : "▸"}`;
|
||||
});
|
||||
wrap.appendChild(header);
|
||||
|
||||
const collapsed = document.createElement("div");
|
||||
collapsed.className = "tool-call-collapsed";
|
||||
liveCalls.slice(-Math.min(2, total)).forEach((call) => {
|
||||
const chip = document.createElement("span");
|
||||
chip.className = "tool-call-chip" + (call.status === "running" ? " tool-call-live-chip--running" : "");
|
||||
chip.textContent = call.name || "unknown";
|
||||
collapsed.appendChild(chip);
|
||||
});
|
||||
if (total > 2) {
|
||||
const more = document.createElement("span");
|
||||
more.className = "tool-call-more";
|
||||
more.textContent = `+${total - 2}`;
|
||||
more.addEventListener("click", () => {
|
||||
liveToolCallExpanded = true;
|
||||
injectLiveToolCalls(typingEl, liveCalls);
|
||||
});
|
||||
collapsed.appendChild(more);
|
||||
}
|
||||
wrap.appendChild(collapsed);
|
||||
|
||||
const expandedArea = document.createElement("div");
|
||||
expandedArea.className = "tool-call-expanded";
|
||||
liveCalls.forEach((call) => {
|
||||
expandedArea.appendChild(renderLiveToolCallDetail(call));
|
||||
});
|
||||
wrap.appendChild(expandedArea);
|
||||
|
||||
scrollChatToBottom();
|
||||
}
|
||||
|
||||
function renderLiveToolCallDetail(call) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "tool-call-detail" + (call.status === "running" ? " tool-call-detail--running" : "");
|
||||
|
||||
const nameEl = document.createElement("div");
|
||||
nameEl.className = "tool-call-detail-name";
|
||||
const statusIcon = call.status === "running" ? "⌛" : "✓";
|
||||
nameEl.textContent = `${statusIcon} ${escapeHtml(call.name || "unknown")}`;
|
||||
card.appendChild(nameEl);
|
||||
|
||||
if (call.args && Object.keys(call.args).length > 0) {
|
||||
const argsEl = document.createElement("pre");
|
||||
argsEl.className = "tool-call-detail-text";
|
||||
argsEl.textContent = JSON.stringify(call.args, null, 2);
|
||||
detail.appendChild(argsEl);
|
||||
card.appendChild(argsEl);
|
||||
}
|
||||
|
||||
if (call.result_text) {
|
||||
const resultEl = document.createElement("pre");
|
||||
resultEl.className = "tool-call-detail-text tool-call-detail-result";
|
||||
resultEl.textContent = call.result_text;
|
||||
detail.appendChild(resultEl);
|
||||
card.appendChild(resultEl);
|
||||
}
|
||||
|
||||
if (call.result_images && call.result_images.length > 0) {
|
||||
call.result_images.forEach((imgUrl) => {
|
||||
if (Array.isArray(call.result_images) && call.result_images.length > 0) {
|
||||
const mediaWrap = document.createElement("div");
|
||||
mediaWrap.className = "tool-call-detail-media";
|
||||
call.result_images.forEach((url) => {
|
||||
if (!url) return;
|
||||
const img = document.createElement("img");
|
||||
img.src = imgUrl;
|
||||
img.className = "tool-call-detail-img";
|
||||
img.className = "tool-call-detail-image";
|
||||
img.src = url;
|
||||
img.alt = "工具返回图片";
|
||||
img.style.maxWidth = "160px";
|
||||
img.style.opacity = "0.5";
|
||||
detail.appendChild(img);
|
||||
img.addEventListener("click", () => window.open(url, "_blank", "noopener"));
|
||||
mediaWrap.appendChild(img);
|
||||
});
|
||||
card.appendChild(mediaWrap);
|
||||
}
|
||||
|
||||
return detail;
|
||||
}
|
||||
|
||||
function injectLiveToolCalls(typingEl, liveCalls) {
|
||||
if (!typingEl || !liveCalls.length) return;
|
||||
// 查找或创建工具调用容器
|
||||
let liveWrap = typingEl.querySelector(".tool-call-live-wrap");
|
||||
if (!liveWrap) {
|
||||
liveWrap = document.createElement("div");
|
||||
liveWrap.className = "tool-call-live-wrap";
|
||||
typingEl.appendChild(liveWrap);
|
||||
}
|
||||
// 更新显示
|
||||
liveWrap.innerHTML = "";
|
||||
liveCalls.forEach((call, i) => {
|
||||
const chip = document.createElement("span");
|
||||
chip.className = "tool-call-live-chip";
|
||||
const statusIcon = call.status === "running" ? "⏳" : "✓";
|
||||
chip.textContent = `${statusIcon} ${escapeHtml(call.name || "")}`;
|
||||
chip.style.opacity = Math.max(0.3, 1 - i * 0.15);
|
||||
if (call.status === "running") chip.classList.add("tool-call-live-chip--running");
|
||||
liveWrap.appendChild(chip);
|
||||
});
|
||||
scrollChatToBottom();
|
||||
}
|
||||
|
||||
function buildToolCallBubble() {
|
||||
const data = toolCallDataCache;
|
||||
if (!data.recent.length && !data.detail.length) return null;
|
||||
|
||||
const total = data.recent.length;
|
||||
const visibleCount = Math.min(2, total);
|
||||
const moreCount = total - visibleCount;
|
||||
|
||||
const wrap = document.createElement("div");
|
||||
wrap.className = "tool-call-bubble-wrap";
|
||||
|
||||
// 折叠按钮
|
||||
const toggleBtn = document.createElement("button");
|
||||
toggleBtn.type = "button";
|
||||
toggleBtn.className = "tool-call-toggle";
|
||||
toggleBtn.textContent = `🔧 工具调用 (${total}) ▸`;
|
||||
toggleBtn.addEventListener("click", () => {
|
||||
const expanded = wrap.classList.toggle("tool-call-bubble-wrap--expanded");
|
||||
toggleBtn.textContent = `🔧 工具调用 (${total}) ${expanded ? "▾" : "▸"}`;
|
||||
});
|
||||
|
||||
// 折叠态:只显示最近2个工具名称,渐变透明度动态消失
|
||||
const collapsed = document.createElement("div");
|
||||
collapsed.className = "tool-call-collapsed";
|
||||
data.recent.slice(-visibleCount).forEach((call, i) => {
|
||||
const chip = document.createElement("span");
|
||||
chip.className = "tool-call-chip";
|
||||
chip.textContent = call.name || "";
|
||||
// 越旧的越透明
|
||||
chip.style.opacity = Math.max(0.25, 1 - (visibleCount - 1 - i) * 0.35);
|
||||
collapsed.appendChild(chip);
|
||||
});
|
||||
if (moreCount > 0) {
|
||||
const more = document.createElement("span");
|
||||
more.className = "tool-call-more";
|
||||
more.textContent = `+${moreCount}`;
|
||||
more.style.cursor = "pointer";
|
||||
more.addEventListener("click", () => {
|
||||
wrap.classList.add("tool-call-bubble-wrap--expanded");
|
||||
toggleBtn.textContent = `🔧 工具调用 (${total}) ▾`;
|
||||
});
|
||||
collapsed.appendChild(more);
|
||||
}
|
||||
|
||||
// 展开态:显示所有详情
|
||||
const expanded = document.createElement("div");
|
||||
expanded.className = "tool-call-expanded";
|
||||
data.detail.forEach((call) => {
|
||||
expanded.appendChild(renderToolCallDetail(call));
|
||||
});
|
||||
|
||||
wrap.appendChild(toggleBtn);
|
||||
wrap.appendChild(collapsed);
|
||||
wrap.appendChild(expanded);
|
||||
|
||||
return wrap;
|
||||
}
|
||||
|
||||
function injectToolCallDisplay(typingEl) {
|
||||
if (!typingEl) return;
|
||||
if (!toolCallDataCache.recent.length && !toolCallDataCache.detail.length) return;
|
||||
|
||||
const existingBlock = typingEl.querySelector(".tool-call-bubble-wrap");
|
||||
if (existingBlock) {
|
||||
// 更新现有显示
|
||||
const newBlock = buildToolCallBubble();
|
||||
if (newBlock) existingBlock.replaceWith(newBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
const block = buildToolCallBubble();
|
||||
if (block) {
|
||||
typingEl.appendChild(block);
|
||||
scrollChatToBottom();
|
||||
}
|
||||
return card;
|
||||
}
|
||||
|
||||
function renderMessage(msg, allowAutoplay = false) {
|
||||
@@ -1381,6 +1349,7 @@
|
||||
const data = await api(`/api/sessions/${encodeURIComponent(id)}/messages`);
|
||||
sessionId = data.sessionId;
|
||||
sessions = data.sessions || [];
|
||||
clearTodoOverlay();
|
||||
renderSessions();
|
||||
renderMessages(data.messages);
|
||||
closeDrawerIfMobile();
|
||||
@@ -1392,6 +1361,7 @@
|
||||
sessions = data.sessions || [];
|
||||
pendingFiles = [];
|
||||
fileInput.value = "";
|
||||
clearTodoOverlay();
|
||||
renderSessions();
|
||||
renderAttachmentChips();
|
||||
renderMessages(data.messages);
|
||||
@@ -1477,8 +1447,9 @@
|
||||
revokeBlobUrlStack(blobUrls);
|
||||
typingEl = appendThinkingIndicator();
|
||||
|
||||
// 清空上轮工具调用缓存(新 turn)
|
||||
toolCallDataCache = { recent: [], detail: [], complete: false };
|
||||
// 清空上轮工具调用状态(新 turn)
|
||||
liveToolCallExpanded = false;
|
||||
clearTodoOverlay();
|
||||
|
||||
// 启动实时工具调用轮询
|
||||
let livePollTimer = null;
|
||||
@@ -1495,15 +1466,37 @@
|
||||
};
|
||||
pollLiveToolCalls();
|
||||
|
||||
// 启动 todo_list 实时轮询
|
||||
let todoPollTimer = null;
|
||||
const pollTodoList = async () => {
|
||||
if (!livePollActive) return;
|
||||
try {
|
||||
const todoData = await api(`/api/todo-list/${encodeURIComponent(sessionId)}`);
|
||||
if (todoData && todoData.has_todo && todoData.data) {
|
||||
updateTodoOverlay(todoData.data);
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
if (livePollActive) todoPollTimer = setTimeout(pollTodoList, 500);
|
||||
};
|
||||
pollTodoList();
|
||||
|
||||
const fd2 = new FormData();
|
||||
fd2.append("session_id", sessionId);
|
||||
fd2.append("output_type", outputModeSelect ? outputModeSelect.value : "text");
|
||||
fd2.append("agent_mode", agentModeSelect ? agentModeSelect.value : "agent");
|
||||
const done = await api("/api/chat/complete", { method: "POST", body: fd2 });
|
||||
|
||||
try {
|
||||
const todoData = await api(`/api/todo-list/${encodeURIComponent(sessionId)}`);
|
||||
if (todoData && todoData.has_todo && todoData.data) {
|
||||
updateTodoOverlay(todoData.data);
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
|
||||
// 停止轮询
|
||||
livePollActive = false;
|
||||
if (livePollTimer) clearTimeout(livePollTimer);
|
||||
if (todoPollTimer) clearTimeout(todoPollTimer);
|
||||
|
||||
sessionId = done.sessionId;
|
||||
sessions = done.sessions || [];
|
||||
@@ -1522,13 +1515,6 @@
|
||||
renderMessages(head);
|
||||
revokeBlobUrlStack(blobUrls);
|
||||
await streamAssistantMessage(last);
|
||||
// 获取工具调用数据并注入到最后一条助手消息
|
||||
await fetchToolCallData();
|
||||
const lastBubble = chatMessages.querySelector(".msg.assistant:last-child .msg-bubble");
|
||||
if (lastBubble && toolCallDataCache.recent.length) {
|
||||
const toolBlock = buildToolCallBubble();
|
||||
if (toolBlock) lastBubble.appendChild(toolBlock);
|
||||
}
|
||||
} else {
|
||||
renderMessages(messages);
|
||||
revokeBlobUrlStack(blobUrls);
|
||||
@@ -2428,13 +2414,6 @@
|
||||
name: a.name,
|
||||
system_prompt: a.system_prompt,
|
||||
max_iterations: a.max_iterations,
|
||||
llm: {
|
||||
model: (a.llm || {}).model || "",
|
||||
api_key: (a.llm || {}).api_key || "",
|
||||
base_url: (a.llm || {}).base_url || "",
|
||||
timeout: (a.llm || {}).timeout || 60,
|
||||
max_retries: (a.llm || {}).max_retries || 0,
|
||||
},
|
||||
llm_model_id: a.llm_model_id || "",
|
||||
tools: a.tools || [],
|
||||
enabled: a.enabled !== false,
|
||||
@@ -2476,366 +2455,6 @@
|
||||
});
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape" && !settingsOverlay.hidden) closeSettings();
|
||||
if (e.key === "Escape" && agentDetailOverlay && !agentDetailOverlay.hidden) closeAgentDetailModal();
|
||||
});
|
||||
|
||||
// ========== 图谱侧面板 ==========
|
||||
const graphToggleBtn = $("graphToggleBtn");
|
||||
const graphPanel = $("graphPanel");
|
||||
const graphCloseBtn = $("graphCloseBtn");
|
||||
const graphCanvas = $("graphCanvas");
|
||||
const graphCanvasWrap = $("graphCanvasWrap");
|
||||
const graphEdgesLayer = $("graphEdgesLayer");
|
||||
const graphNodesLayer = $("graphNodesLayer");
|
||||
const graphRefreshBtn = $("graphRefreshBtn");
|
||||
const graphFitBtn = $("graphFitBtn");
|
||||
|
||||
// 图谱数据与状态
|
||||
let graphData = { agents: [], edges: [], events: [], tool_calls: {} };
|
||||
let graphOpen = false;
|
||||
// 可拖拽偏移
|
||||
let panOffset = { x: 80, y: 60 };
|
||||
let isPanning = false;
|
||||
let panStart = { x: 0, y: 0 };
|
||||
let panStartOffset = { x: 0, y: 0 };
|
||||
// 节点布局参数
|
||||
const NODE_W = 160;
|
||||
const NODE_H = 100;
|
||||
|
||||
// 图谱开关(始终可聊天)
|
||||
function toggleGraph(open) {
|
||||
graphOpen = open;
|
||||
graphPanel.hidden = !open;
|
||||
graphToggleBtn.classList.toggle("graph-active", open);
|
||||
appRoot.classList.toggle("has-graph", open);
|
||||
if (open) {
|
||||
loadGraphData();
|
||||
}
|
||||
}
|
||||
|
||||
graphToggleBtn.addEventListener("click", () => toggleGraph(!graphOpen));
|
||||
graphCloseBtn.addEventListener("click", () => toggleGraph(false));
|
||||
|
||||
// Agent 详情弹窗事件
|
||||
const agentDetailOverlay = document.getElementById("agentDetailOverlay");
|
||||
const agentDetailClose = document.getElementById("agentDetailClose");
|
||||
if (agentDetailClose) agentDetailClose.addEventListener("click", closeAgentDetailModal);
|
||||
if (agentDetailOverlay) agentDetailOverlay.addEventListener("click", (e) => {
|
||||
if (e.target === agentDetailOverlay) closeAgentDetailModal();
|
||||
});
|
||||
|
||||
// 加载图谱数据
|
||||
async function loadGraphData() {
|
||||
try {
|
||||
const snap = await api(`/api/graph/snapshot?session_id=${encodeURIComponent(sessionId)}`);
|
||||
graphData = snap;
|
||||
renderGraph();
|
||||
} catch (e) {
|
||||
console.error("加载图谱数据失败:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 自动布局:主 agent 在中心,子 agent 环绕
|
||||
function computeLayout() {
|
||||
const agents = graphData.agents || [];
|
||||
if (agents.length === 0) return {};
|
||||
|
||||
const positions = {};
|
||||
const wrapW = graphCanvasWrap.clientWidth || 600;
|
||||
const wrapH = graphCanvasWrap.clientHeight || 400;
|
||||
const cx = wrapW / 2;
|
||||
const cy = wrapH / 2;
|
||||
|
||||
// 主 agent 在中心
|
||||
const mainAgent = agents.find(a => a.role === "main");
|
||||
if (mainAgent) {
|
||||
positions[mainAgent.name] = { x: cx - NODE_W / 2, y: cy - NODE_H / 2 };
|
||||
}
|
||||
|
||||
// 子 agent 均匀环绕
|
||||
const subs = agents.filter(a => a.role !== "main");
|
||||
const radius = Math.min(wrapW, wrapH) * 0.32;
|
||||
subs.forEach((agent, i) => {
|
||||
const angle = (2 * Math.PI * i) / subs.length - Math.PI / 2;
|
||||
positions[agent.name] = {
|
||||
x: cx + radius * Math.cos(angle) - NODE_W / 2,
|
||||
y: cy + radius * Math.sin(angle) - NODE_H / 2,
|
||||
};
|
||||
});
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
// 状态到 CSS 类 + 文字
|
||||
function statusInfo(status) {
|
||||
switch (status) {
|
||||
case "running": return { cls: "status-running", badge: "执行中" };
|
||||
case "done": return { cls: "status-done", badge: "已完成" };
|
||||
case "error": return { cls: "status-error", badge: "出错" };
|
||||
default: return { cls: "status-idle", badge: "等待" };
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染图谱
|
||||
function renderGraph() {
|
||||
const agents = graphData.agents || [];
|
||||
const edges = graphData.edges || [];
|
||||
const positions = computeLayout();
|
||||
|
||||
// 计算 SVG viewBox
|
||||
const wrapW = graphCanvasWrap.clientWidth || 600;
|
||||
const wrapH = graphCanvasWrap.clientHeight || 400;
|
||||
graphCanvas.setAttribute("viewBox", `0 0 ${wrapW} ${wrapH}`);
|
||||
|
||||
// --- 渲染连线 ---
|
||||
graphEdgesLayer.innerHTML = "";
|
||||
edges.forEach((edge, i) => {
|
||||
const srcPos = positions[edge.source];
|
||||
const tgtPos = positions[edge.target];
|
||||
if (!srcPos || !tgtPos) return;
|
||||
|
||||
const x1 = srcPos.x + NODE_W / 2;
|
||||
const y1 = srcPos.y + NODE_H / 2;
|
||||
const x2 = tgtPos.x + NODE_W / 2;
|
||||
const y2 = tgtPos.y + NODE_H / 2;
|
||||
|
||||
// 计算从边缘出发(而非中心)
|
||||
const dx = x2 - x1;
|
||||
const dy = y2 - y1;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy) || 1;
|
||||
const nx = dx / dist;
|
||||
const ny = dy / dist;
|
||||
|
||||
const sx = x1 + nx * (NODE_W / 2);
|
||||
const sy = y1 + ny * (NODE_H / 2);
|
||||
const ex = x2 - nx * (NODE_W / 2);
|
||||
const ey = y2 - ny * (NODE_H / 2);
|
||||
|
||||
let edgeClass = "normal";
|
||||
let markerEnd = "url(#arrowNormal)";
|
||||
if (edge.highlighted) {
|
||||
edgeClass = "highlighted";
|
||||
markerEnd = "url(#arrowHighlight)";
|
||||
}
|
||||
|
||||
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
||||
line.setAttribute("x1", sx + panOffset.x);
|
||||
line.setAttribute("y1", sy + panOffset.y);
|
||||
line.setAttribute("x2", ex + panOffset.x);
|
||||
line.setAttribute("y2", ey + panOffset.y);
|
||||
line.setAttribute("class", `graph-edge ${edgeClass}`);
|
||||
line.setAttribute("marker-end", markerEnd);
|
||||
line.setAttribute("data-source", edge.source);
|
||||
line.setAttribute("data-target", edge.target);
|
||||
graphEdgesLayer.appendChild(line);
|
||||
});
|
||||
|
||||
// --- 渲染节点 ---
|
||||
graphNodesLayer.innerHTML = "";
|
||||
agents.forEach((agent) => {
|
||||
const pos = positions[agent.name];
|
||||
if (!pos) return;
|
||||
|
||||
const sinfo = statusInfo(agent.status || "idle");
|
||||
const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
||||
g.setAttribute("class", `graph-node ${sinfo.cls}`);
|
||||
g.setAttribute("transform", `translate(${pos.x + panOffset.x}, ${pos.y + panOffset.y})`);
|
||||
g.setAttribute("data-agent", agent.name);
|
||||
|
||||
// 圆角矩形
|
||||
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
||||
rect.setAttribute("width", NODE_W);
|
||||
rect.setAttribute("height", NODE_H);
|
||||
rect.setAttribute("class", "graph-node-rect");
|
||||
g.appendChild(rect);
|
||||
|
||||
// 图标
|
||||
const iconText = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
||||
iconText.setAttribute("x", 28);
|
||||
iconText.setAttribute("y", NODE_H / 2);
|
||||
iconText.setAttribute("class", "graph-node-icon");
|
||||
iconText.textContent = agent.icon || "🧠";
|
||||
g.appendChild(iconText);
|
||||
|
||||
// 名称
|
||||
const labelText = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
||||
labelText.setAttribute("x", 90);
|
||||
labelText.setAttribute("y", NODE_H / 2 - 8);
|
||||
labelText.setAttribute("class", "graph-node-label");
|
||||
labelText.textContent = agent.name;
|
||||
g.appendChild(labelText);
|
||||
|
||||
// 角色
|
||||
const roleText = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
||||
roleText.setAttribute("x", 90);
|
||||
roleText.setAttribute("y", NODE_H / 2 + 12);
|
||||
roleText.setAttribute("class", "graph-node-role");
|
||||
roleText.textContent = agent.role === "main" ? "主 Agent" : "子 Agent";
|
||||
g.appendChild(roleText);
|
||||
|
||||
// 状态标签(右上角)
|
||||
const badgeText = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
||||
badgeText.setAttribute("x", NODE_W - 8);
|
||||
badgeText.setAttribute("y", 14);
|
||||
badgeText.setAttribute("class", "graph-node-badge");
|
||||
const badgeColor = agent.status === "running" ? "#f59e0b" : agent.status === "done" ? "#22c55e" : agent.status === "error" ? "#ef4444" : "#94a3b8";
|
||||
badgeText.setAttribute("fill", badgeColor);
|
||||
badgeText.textContent = sinfo.badge;
|
||||
g.appendChild(badgeText);
|
||||
|
||||
// 工具调用缩略(仅最近2轮,渐变透明)
|
||||
const agentToolCalls = (graphData.tool_calls || {})[agent.name] || [];
|
||||
const recentCalls = agentToolCalls.slice(0, 4);
|
||||
recentCalls.forEach((tc, idx) => {
|
||||
const chip = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
||||
chip.setAttribute("x", 12 + idx * 38);
|
||||
chip.setAttribute("y", NODE_H - 10);
|
||||
chip.setAttribute("class", "graph-node-tool-chip");
|
||||
chip.setAttribute("opacity", Math.max(0.2, 1 - idx * 0.35));
|
||||
chip.textContent = (tc.tool_name || "").substring(0, 5);
|
||||
g.appendChild(chip);
|
||||
});
|
||||
|
||||
// 点击事件 → 弹出 Agent 详情 modal
|
||||
g.style.cursor = "pointer";
|
||||
g.addEventListener("click", () => openAgentDetailModal(agent));
|
||||
|
||||
graphNodesLayer.appendChild(g);
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染工具调用列表 → 已移至 Agent 点击弹窗
|
||||
function openAgentDetailModal(agent) {
|
||||
const overlay = document.getElementById("agentDetailOverlay");
|
||||
const stateDiv = document.getElementById("agentDetailState");
|
||||
const configDiv = document.getElementById("agentDetailConfig");
|
||||
const toolCallsDiv = document.getElementById("agentDetailToolCalls");
|
||||
const titleEl = document.getElementById("agentDetailTitle");
|
||||
if (!overlay || !stateDiv || !configDiv || !toolCallsDiv) return;
|
||||
|
||||
titleEl.textContent = `Agent: ${agent.name}`;
|
||||
const sinfo = statusInfo(agent.status || "idle");
|
||||
|
||||
// 状态
|
||||
stateDiv.innerHTML = `
|
||||
<p><strong>名称:</strong> ${escapeHtml(agent.name)}</p>
|
||||
<p><strong>角色:</strong> ${agent.role === "main" ? "主 Agent" : "子 Agent"}</p>
|
||||
<p><strong>状态:</strong> <span style="color:${agent.status === 'running' ? '#f59e0b' : agent.status === 'done' ? '#22c55e' : agent.status === 'error' ? '#ef4444' : '#94a3b8'}">${sinfo.badge}</span></p>
|
||||
${agent.error_message ? `<p><strong>错误:</strong> <span style="color:#ef4444">${escapeHtml(agent.error_message)}</span></p>` : ""}
|
||||
`;
|
||||
|
||||
// 配置(只读)
|
||||
const tools = agent.tools || [];
|
||||
configDiv.innerHTML = `
|
||||
<p><strong>工具列表:</strong> ${tools.length ? tools.map(t => `<code>${escapeHtml(t)}</code>`).join(", ") : "(无)"}</p>
|
||||
`;
|
||||
|
||||
// 工具调用记录
|
||||
const calls = (graphData.tool_calls || {})[agent.name] || [];
|
||||
if (calls.length === 0) {
|
||||
toolCallsDiv.innerHTML = `<p style="color:#94a3b8;font-style:italic;">暂无工具调用记录</p>`;
|
||||
} else {
|
||||
toolCallsDiv.innerHTML = [...calls].reverse().slice(0, 6).map(c => `
|
||||
<div class="toolcall-card">
|
||||
<div class="toolcall-card-head">
|
||||
<span class="toolcall-card-name">${escapeHtml(c.tool_name)}</span>
|
||||
<span class="toolcall-card-status ${c.status || 'success'}">${c.status || 'success'}</span>
|
||||
</div>
|
||||
${c.tool_args && Object.keys(c.tool_args).length > 0 ? `<div class="toolcall-card-args">${escapeHtml(JSON.stringify(c.tool_args, null, 1))}</div>` : ""}
|
||||
${c.result_preview ? `<div class="toolcall-card-result">${escapeHtml(c.result_preview)}</div>` : ""}
|
||||
</div>
|
||||
`).join("");
|
||||
}
|
||||
|
||||
overlay.hidden = false;
|
||||
}
|
||||
|
||||
// 关闭 Agent 详情弹窗
|
||||
function closeAgentDetailModal() {
|
||||
const overlay = document.getElementById("agentDetailOverlay");
|
||||
if (overlay) overlay.hidden = true;
|
||||
}
|
||||
|
||||
|
||||
// 画布拖拽
|
||||
graphCanvasWrap.addEventListener("mousedown", (e) => {
|
||||
if (e.target === graphCanvas || e.target === graphCanvasWrap || e.target.closest("svg")) {
|
||||
isPanning = true;
|
||||
panStart = { x: e.clientX, y: e.clientY };
|
||||
panStartOffset = { ...panOffset };
|
||||
graphCanvasWrap.classList.add("panning");
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("mousemove", (e) => {
|
||||
if (!isPanning) return;
|
||||
const dx = e.clientX - panStart.x;
|
||||
const dy = e.clientY - panStart.y;
|
||||
panOffset = {
|
||||
x: panStartOffset.x + dx,
|
||||
y: panStartOffset.y + dy,
|
||||
};
|
||||
renderGraph();
|
||||
});
|
||||
|
||||
window.addEventListener("mouseup", () => {
|
||||
if (isPanning) {
|
||||
isPanning = false;
|
||||
graphCanvasWrap.classList.remove("panning");
|
||||
}
|
||||
});
|
||||
|
||||
// 触摸拖拽
|
||||
graphCanvasWrap.addEventListener("touchstart", (e) => {
|
||||
if (e.touches.length === 1) {
|
||||
isPanning = true;
|
||||
panStart = { x: e.touches[0].clientX, y: e.touches[0].clientY };
|
||||
panStartOffset = { ...panOffset };
|
||||
graphCanvasWrap.classList.add("panning");
|
||||
}
|
||||
}, { passive: false });
|
||||
|
||||
graphCanvasWrap.addEventListener("touchmove", (e) => {
|
||||
if (!isPanning || e.touches.length !== 1) return;
|
||||
const dx = e.touches[0].clientX - panStart.x;
|
||||
const dy = e.touches[0].clientY - panStart.y;
|
||||
panOffset = {
|
||||
x: panStartOffset.x + dx,
|
||||
y: panStartOffset.y + dy,
|
||||
};
|
||||
renderGraph();
|
||||
e.preventDefault();
|
||||
}, { passive: false });
|
||||
|
||||
graphCanvasWrap.addEventListener("touchend", () => {
|
||||
if (isPanning) {
|
||||
isPanning = false;
|
||||
graphCanvasWrap.classList.remove("panning");
|
||||
}
|
||||
});
|
||||
|
||||
// 鼠标滚轮缩放(可选)
|
||||
|
||||
// 按钮事件
|
||||
graphRefreshBtn.addEventListener("click", () => {
|
||||
loadGraphData();
|
||||
});
|
||||
|
||||
graphFitBtn.addEventListener("click", () => {
|
||||
panOffset = { x: 80, y: 60 };
|
||||
renderGraph();
|
||||
});
|
||||
|
||||
// 窗口大小变化时重新渲染
|
||||
let resizeGraphTimer = null;
|
||||
window.addEventListener("resize", () => {
|
||||
clearTimeout(resizeGraphTimer);
|
||||
resizeGraphTimer = setTimeout(() => {
|
||||
if (graphOpen) {
|
||||
renderGraph();
|
||||
}
|
||||
}, 200);
|
||||
});
|
||||
|
||||
bootstrap().catch((e) => {
|
||||
|
||||
@@ -86,18 +86,8 @@
|
||||
>
|
||||
⚙
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="settings-btn graph-toggle-btn"
|
||||
id="graphToggleBtn"
|
||||
title="打开 Agent 图谱"
|
||||
aria-label="打开 Agent 图谱"
|
||||
>
|
||||
🔗
|
||||
</button>
|
||||
</header>
|
||||
<div class="main-content-wrap" id="mainContentWrap">
|
||||
<!-- 聊天面板(始终可见) -->
|
||||
<section class="chat-shell" id="chatPanel">
|
||||
<div class="chat-placeholder" id="chatPlaceholder">
|
||||
<h3>开始对话</h3>
|
||||
@@ -106,39 +96,6 @@
|
||||
</div>
|
||||
<div class="chat-messages" id="chatMessages" hidden></div>
|
||||
</section>
|
||||
<!-- 图谱侧面板(默认隐藏) -->
|
||||
<aside class="graph-panel" id="graphPanel" hidden>
|
||||
<div class="graph-toolbar">
|
||||
<span class="graph-toolbar-title">Agent 图谱</span>
|
||||
<button type="button" class="btn btn-ghost graph-btn" id="graphRefreshBtn" title="刷新图谱">🔄</button>
|
||||
<button type="button" class="btn btn-ghost graph-btn" id="graphFitBtn" title="适应画布">⊞</button>
|
||||
<span class="graph-legend">
|
||||
<span class="legend-dot legend-running"></span> 执行中
|
||||
<span class="legend-dot legend-done"></span> 已完成
|
||||
<span class="legend-dot legend-error"></span> 出错
|
||||
<span class="legend-dot legend-idle"></span> 等待
|
||||
</span>
|
||||
<button type="button" class="graph-close-btn" id="graphCloseBtn" title="关闭图谱" aria-label="关闭图谱">✕</button>
|
||||
</div>
|
||||
<div class="graph-canvas-wrap" id="graphCanvasWrap">
|
||||
<svg class="graph-canvas" id="graphCanvas">
|
||||
<defs>
|
||||
<marker id="arrowHighlight" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#f59e0b" />
|
||||
</marker>
|
||||
<marker id="arrowNormal" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="rgba(148,163,184,0.5)" />
|
||||
</marker>
|
||||
<marker id="arrowActive" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#14b8a6" />
|
||||
</marker>
|
||||
</defs>
|
||||
<g id="graphEdgesLayer"></g>
|
||||
<g id="graphNodesLayer"></g>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 工具调用详情已移至 Agent 点击弹窗 -->
|
||||
</aside>
|
||||
</div>
|
||||
<footer class="composer">
|
||||
<div class="composer-attachments" id="attachmentPreview"></div>
|
||||
@@ -187,29 +144,6 @@
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
<!-- Agent 详情弹窗(图谱中点击 Agent 节点) -->
|
||||
<div class="agent-detail-overlay" id="agentDetailOverlay" hidden>
|
||||
<div class="agent-detail-dialog" id="agentDetailDialog">
|
||||
<div class="agent-detail-header">
|
||||
<h2 id="agentDetailTitle">Agent 详情</h2>
|
||||
<button type="button" class="agent-detail-close" id="agentDetailClose">×</button>
|
||||
</div>
|
||||
<div class="agent-detail-body">
|
||||
<div class="agent-detail-section">
|
||||
<h3>状态</h3>
|
||||
<div id="agentDetailState"></div>
|
||||
</div>
|
||||
<div class="agent-detail-section">
|
||||
<h3>配置</h3>
|
||||
<div id="agentDetailConfig"></div>
|
||||
</div>
|
||||
<div class="agent-detail-section">
|
||||
<h3>工具调用记录</h3>
|
||||
<div id="agentDetailToolCalls"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 系统配置模态框 -->
|
||||
<div class="settings-overlay" id="settingsOverlay" hidden>
|
||||
<div class="settings-dialog" id="settingsDialog" role="dialog" aria-modal="true" aria-label="系统配置">
|
||||
|
||||
+34
-153
@@ -9,7 +9,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import json
|
||||
import mimetypes
|
||||
import os
|
||||
import shutil
|
||||
@@ -325,11 +324,13 @@ def api_clear_session(session_id: str = Form(...)) -> dict[str, Any]:
|
||||
{"sessionId": str, "sessions": list[str], "messages": []}
|
||||
|
||||
系统定位:
|
||||
前端“清空对话”按钮调用。
|
||||
前端"清空对话"按钮调用。
|
||||
"""
|
||||
if not session_id:
|
||||
raise HTTPException(status_code=400, detail="缺少 session_id")
|
||||
messages, _ = ui.clear_ui_and_manifest(session_id)
|
||||
# 清理 tool_calling 目录
|
||||
_clear_tool_calling(session_id)
|
||||
ids = ui.list_session_ids_ordered()
|
||||
if session_id not in ids:
|
||||
ids = [session_id, *ids]
|
||||
@@ -352,6 +353,7 @@ def api_delete_session(session_id: str = Form(...)) -> dict[str, Any]:
|
||||
if not session_id:
|
||||
raise HTTPException(status_code=400, detail="缺少 session_id")
|
||||
ui.delete_session(session_id)
|
||||
_clear_tool_calling(session_id)
|
||||
sid, ids, hist = _bootstrap()
|
||||
return {"sessionId": sid, "sessions": ids, "messages": _serialize_messages(hist)}
|
||||
|
||||
@@ -433,8 +435,8 @@ def _chat_start_core(session_id: str, query: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
hist = ui.load_ui_messages_for_session(session_id)
|
||||
before = len(hist or [])
|
||||
new_hist, cleared = ui.add_message(hist or [], query)
|
||||
if cleared is None:
|
||||
new_hist = ui.add_message(hist or [], query)
|
||||
if new_hist is None:
|
||||
raise HTTPException(status_code=400, detail="请输入文字或上传图片/音频")
|
||||
if len(new_hist) == before:
|
||||
raise HTTPException(status_code=400, detail="请输入文字或上传图片/音频")
|
||||
@@ -547,43 +549,7 @@ def api_chat_complete(
|
||||
return _chat_complete_core(session_id, output_type=output_type, agent_mode=agent_mode)
|
||||
|
||||
|
||||
@app.post("/api/chat")
|
||||
async def api_chat(
|
||||
session_id: str = Form(...),
|
||||
text: str = Form(""),
|
||||
images: list[UploadFile] | None = File(None),
|
||||
files: list[UploadFile] | None = File(None),
|
||||
audio: UploadFile | None = File(None),
|
||||
output_type: str = Form("text"),
|
||||
agent_mode: str = Form("agent"),
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
兼容旧客户端:等价于依次 ``/api/chat/start`` + ``/api/chat/complete``(仍阻塞至推理结束)。
|
||||
|
||||
输入:
|
||||
session_id: 表单字段,会话 ID。
|
||||
text: 用户输入的文本。
|
||||
images: 上传的图片文件列表。
|
||||
files: 上传的普通文件列表。
|
||||
audio: 上传的音频文件。
|
||||
output_type: text | audio | both。
|
||||
agent_mode: agent | plan | team。
|
||||
|
||||
输出:
|
||||
{"sessionId": str, "sessions": list[str], "messages": list[dict]}
|
||||
|
||||
系统定位:
|
||||
向后兼容的一次性对话接口。
|
||||
"""
|
||||
if not session_id:
|
||||
raise HTTPException(status_code=400, detail="缺少 session_id")
|
||||
|
||||
query, tmp_root = _materialize_uploads_to_temp(images, files, audio, text)
|
||||
try:
|
||||
_chat_start_core(session_id, query)
|
||||
return _chat_complete_core(session_id, output_type=output_type, agent_mode=agent_mode)
|
||||
finally:
|
||||
shutil.rmtree(tmp_root, ignore_errors=True)
|
||||
|
||||
|
||||
@app.post("/api/human-action")
|
||||
@@ -635,15 +601,16 @@ def api_get_agent_configs() -> dict[str, Any]:
|
||||
|
||||
输出: {"agents": list[dict], "config_path": str}
|
||||
"""
|
||||
from agent.core.config_manager import load_models
|
||||
configs = load_agent_configs()
|
||||
models = {m["id"]: m for m in load_models()}
|
||||
return {
|
||||
"agents": [{"name": c.name, "system_prompt": c.system_prompt, "max_iterations": c.max_iterations,
|
||||
"llm": {"model": c.llm.model, "api_key": c.llm.api_key, "base_url": c.llm.base_url,
|
||||
"timeout": c.llm.timeout, "max_retries": c.llm.max_retries},
|
||||
"llm_model_id": c.llm_model_id,
|
||||
"tools": c.tools,
|
||||
"enabled": c.enabled,
|
||||
"role": c.role} for c in configs],
|
||||
"models": list(models.values()),
|
||||
"config_path": str(AGENT_CONFIG_PATH),
|
||||
}
|
||||
|
||||
@@ -795,70 +762,10 @@ def api_reload_configs() -> dict[str, Any]:
|
||||
return {"success": False, "agent": {}, "detail": str(e)}
|
||||
|
||||
|
||||
# ---------- 多 Agent 图面板 API ----------
|
||||
@app.get("/api/graph/snapshot")
|
||||
def api_get_graph_snapshot(session_id: str = "") -> dict[str, Any]:
|
||||
"""获取当前多 Agent 运行时的图结构快照,供前端 graph 面板渲染。"""
|
||||
try:
|
||||
from agent.agents.multi_agent_runtime import get_agent_graph_snapshot, build_multi_agent_runtime
|
||||
from agent.agents.main_agent import main_agent_runtime
|
||||
from agent.history.tool_call_recorder import get_tool_calls_for_latest_turn
|
||||
runtime = build_multi_agent_runtime(main_runtime=main_agent_runtime)
|
||||
result = get_agent_graph_snapshot(runtime)
|
||||
if session_id:
|
||||
recorded = get_tool_calls_for_latest_turn(session_id)
|
||||
if recorded:
|
||||
existing = result.get("tool_calls", {}) or {}
|
||||
main_calls = existing.get("main", [])
|
||||
for r in recorded:
|
||||
main_calls.append({
|
||||
"tool_name": r.get("name", ""),
|
||||
"tool_args": {},
|
||||
"result_preview": r.get("result_text", "")[:80],
|
||||
"status": r.get("status", "done"),
|
||||
})
|
||||
result["tool_calls"] = {**existing, "main": main_calls}
|
||||
return result
|
||||
except Exception as e:
|
||||
return {
|
||||
"agents": [
|
||||
{"name": "main", "role": "main", "icon": "🧠", "status": "idle", "error_message": ""}
|
||||
],
|
||||
"edges": [],
|
||||
"events": [],
|
||||
"tool_calls": {},
|
||||
"error": str(e),
|
||||
}
|
||||
|
||||
|
||||
@app.get("/api/graph/config")
|
||||
def api_get_graph_config() -> dict[str, Any]:
|
||||
"""获取 graph 面板的初始配置(从 agent_config.json 读取各 agent 的 role/icon 等)。
|
||||
|
||||
输出:
|
||||
{"agents": [{"name": str, "role": str, "icon": str}, ...]}
|
||||
"""
|
||||
from agent.core.config_manager import load_agent_configs
|
||||
configs = load_agent_configs()
|
||||
role_icons = {"main": "🧠", "code": "💻", "research": "🔍", "doc": "📄", "gui": "🖥"}
|
||||
agents_list = []
|
||||
for cfg in configs:
|
||||
if cfg.enabled:
|
||||
agents_list.append({
|
||||
"name": cfg.name,
|
||||
"role": cfg.role,
|
||||
"icon": role_icons.get(cfg.name, "🔧"),
|
||||
"tools": cfg.tools,
|
||||
})
|
||||
if not agents_list:
|
||||
agents_list = [{"name": "main", "role": "main", "icon": "🧠", "tools": []}]
|
||||
return {"agents": agents_list}
|
||||
|
||||
|
||||
# ---------- 工具调用历史 API ----------
|
||||
@app.get("/api/tool-calls/{session_id}/live")
|
||||
def api_get_live_tool_calls(session_id: str) -> dict[str, Any]:
|
||||
"""获取当前进行中的工具调用(图表执行期间实时轮询)。"""
|
||||
"""获取当前进行中的工具调用(前端执行期间实时轮询)。"""
|
||||
try:
|
||||
from agent.history.tool_call_recorder import get_live_tool_calls
|
||||
return {"tool_calls": get_live_tool_calls(session_id)}
|
||||
@@ -866,61 +773,35 @@ def api_get_live_tool_calls(session_id: str) -> dict[str, Any]:
|
||||
return {"tool_calls": [], "error": str(e)}
|
||||
|
||||
|
||||
@app.get("/api/tool-calls/{session_id}/recent")
|
||||
def api_get_recent_tool_calls(session_id: str) -> dict[str, Any]:
|
||||
"""获取最新 turn 的工具调用缩略(仅名称,无返回值)。"""
|
||||
# ---------- Todo List 实时推送 ----------
|
||||
# 数据直接从 agent.tools.tool 的共享 store 读取,无需额外存储
|
||||
|
||||
@app.get("/api/todo-list/{session_id}")
|
||||
def api_get_todo_list(session_id: str) -> dict[str, Any]:
|
||||
"""获取当前会话最新的 todo list 数据(前端轮询)。"""
|
||||
try:
|
||||
from agent.history.tool_call_recorder import get_tool_calls_for_latest_turn
|
||||
calls = get_tool_calls_for_latest_turn(session_id)
|
||||
result = []
|
||||
for c in calls:
|
||||
result.append({
|
||||
"name": c.get("name", ""),
|
||||
"args_summary": json.dumps(c.get("args", {}), ensure_ascii=False)[:40],
|
||||
"status": "done",
|
||||
"turn_id": c.get("turn_id", ""),
|
||||
})
|
||||
return {"tool_calls": result}
|
||||
except Exception as e:
|
||||
return {"tool_calls": [], "error": str(e)}
|
||||
from agent.tools.tool import get_todo_store_data
|
||||
data = get_todo_store_data(session_id)
|
||||
except Exception:
|
||||
data = None
|
||||
if data:
|
||||
return {"has_todo": True, "data": data}
|
||||
return {"has_todo": False, "data": None}
|
||||
|
||||
|
||||
@app.get("/api/tool-calls/{session_id}/detail")
|
||||
def api_get_tool_call_detail(session_id: str) -> dict[str, Any]:
|
||||
"""获取最新 turn 的工具调用详情(含返回值和图片 base64)。"""
|
||||
def _clear_tool_calling(session_id: str) -> None:
|
||||
"""清理会话关联的 tool_calling 目录与 todo_list 缓存。"""
|
||||
try:
|
||||
from agent.history.tool_call_recorder import get_tool_calls_for_latest_turn
|
||||
details = get_tool_calls_for_latest_turn(session_id)
|
||||
# 图片文件转 data URL
|
||||
import base64
|
||||
for d in details:
|
||||
imgs = []
|
||||
for fp in d.get("result_files_full", []):
|
||||
if os.path.isfile(fp):
|
||||
with open(fp, "rb") as f:
|
||||
b64 = base64.standard_b64encode(f.read()).decode("ascii")
|
||||
ext = os.path.splitext(fp)[1].lower()
|
||||
mime_map = {".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif"}
|
||||
mime = mime_map.get(ext, "image/png")
|
||||
imgs.append(f"data:{mime};base64,{b64}")
|
||||
d["result_images"] = imgs
|
||||
return {"details": details}
|
||||
except Exception as e:
|
||||
return {"details": [], "error": str(e)}
|
||||
|
||||
|
||||
@app.get("/api/tool-calls/{session_id}/has-pending")
|
||||
def api_has_pending_tool_calls(session_id: str) -> dict[str, Any]:
|
||||
"""检查是否有待处理的工具调用(thinking 状态时使用)。"""
|
||||
from agent.tools.tool import clear_todo_store_session
|
||||
clear_todo_store_session(session_id)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
from agent.history.tool_call_recorder import list_turns
|
||||
turns = list_turns(session_id)
|
||||
has_tools = bool(turns)
|
||||
# 判断最新 turn 是否已完成(有 assistant_response)
|
||||
latest_complete = turns[0].get("assistant_response", "") if turns else ""
|
||||
return {"has_recent": has_tools, "is_complete": bool(latest_complete)}
|
||||
except Exception as e:
|
||||
return {"has_recent": False, "is_complete": True, "error": str(e)}
|
||||
import shutil as _shutil
|
||||
from agent.history.tool_call_recorder import session_tool_dir
|
||||
_shutil.rmtree(str(session_tool_dir(session_id)), ignore_errors=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@app.get("/favicon.ico")
|
||||
|
||||
@@ -142,6 +142,12 @@ def delete_session(session_id: str) -> None:
|
||||
conn.close()
|
||||
_memory_session_ids.discard(session_id)
|
||||
shutil.rmtree(session_dir(session_id), ignore_errors=True)
|
||||
# 清理 tool_calling 目录
|
||||
try:
|
||||
from agent.history.tool_call_recorder import session_tool_dir
|
||||
shutil.rmtree(str(session_tool_dir(session_id)), ignore_errors=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _lc_content_for_ui_messages(content) -> str | list:
|
||||
@@ -701,8 +707,7 @@ def add_message(chat_history, query):
|
||||
query: {"text": str, "files": list[str], "file_names": list[str]}
|
||||
|
||||
输出:
|
||||
(new_chat_history, cleared_query) 元组;
|
||||
cleared_query 在旧 API 兼容中保留,新调用可忽略。
|
||||
成功返回 new_chat_history,无有效内容返回 None。
|
||||
|
||||
系统定位:
|
||||
API 层构造用户消息的核心函数。
|
||||
@@ -710,9 +715,9 @@ def add_message(chat_history, query):
|
||||
chat_history = chat_history or []
|
||||
user_content = _build_user_chat_content(query)
|
||||
if user_content is None:
|
||||
return chat_history, None
|
||||
return None
|
||||
chat_history.append({"role": "user", "content": user_content})
|
||||
return chat_history, {"text": "", "files": []}
|
||||
return chat_history
|
||||
|
||||
|
||||
def clear_ui_and_manifest(session_id: str):
|
||||
|
||||
Reference in new issue
Block a user