xiaoli-cli 7.0.0: 重构底层架构 + 移除工具截断

- 重构 core/ 底层 (7文件): app/runner/engine/parser/history/safety/display
- FC原生调用为主,prompt-JSON保底
- 移除所有工具返回截断 (ai_search/git_tools/cmd_executor/browser_auto/network_tools/sub_agent/tool_search)
- 删除无用文件 (旧服务器/WebUI/彩蛋/启动器等21个文件)
- 清理 constants.py 死代码
- 版本号更新为 7.0.0
This commit is contained in:
shuiyu1123 committed 2026-07-05 13:17:27 +08:00
commit 78fea7ccdd
57 files changed
+18358

No files matched your search

+219
View File
@@ -0,0 +1,219 @@
import json
import time
from urllib.parse import quote
import requests
class Liugin:
"""网页内容工具 — jina.ai 代理 + AI 直连"""
_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36'
_HEADERS = {
'User-Agent': _UA,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
}
def __init__(self):
self.usage = """网页内容工具
【jina.ai 代理组】— 通过 jina.ai 服务,返回干净文本:
ai_url <网址> - jina 提取网页正文
ai_search <搜索词> - jina 网络搜索
ai_web <搜索词> - jina 网络搜索(同 ai_search)
【AI 直连组】— 直接 HTTP 请求,模型自行处理:
url <网址> - 直接获取网页原始内容
search <搜索词> - 直接搜索(模型自行选择搜索引擎)
web <搜索词> - 同 search
JSON 格式示例:
{"action": "use_tool", "tool": "ai_search", "args": "ai_url https://www.example.com"}
{"action": "use_tool", "tool": "ai_search", "args": "ai_search Python教程"}
{"action": "use_tool", "tool": "ai_search", "args": "url https://api.example.com/data"}
⚠️ 使用原则:
- 优先使用 AI 直连组 (url/search/web),自己获取和处理网页内容
- 仅当直连失败、网页需要 JS 渲染、或需要 jina 的智能提取时才用 jina 代理组
- 不要滥用 jina,它有速率限制
"""
self.cli = None
self.timeout = 30
self.max_retries = 2
def set_cli(self, cli):
self.cli = cli
self.cli.register_liugin_command('ai_search', self.command_handler)
def command_handler(self, args):
parts = args.strip().split(maxsplit=1)
if len(parts) < 1:
return "请提供操作: ai_url/ai_search/ai_web/url/search/web <参数>"
operation = parts[0].lower()
remaining_args = parts[1] if len(parts) > 1 else ""
return self.handle(f"{operation} {remaining_args}")
def get_tool_info(self):
return {
"name": "ai_search",
"description": "网页内容工具。ai_url/ai_search/ai_web 用 jina.ai 代理;url/search/web 直接 HTTP 请求让模型自行处理。优先用直连组,jina 仅作备用。",
"keywords": ["网址", "url", "网页", "web", "search", "搜索", "fetch", "提取", "jina",
"ai_url", "ai_search", "ai_web", "内容", "获取", "联网"],
"usage": self.usage
}
def handle(self, args):
try:
parts = args.strip().split(maxsplit=1)
if len(parts) < 1:
return "错误:参数不足。可用操作: ai_url/ai_search/ai_web/url/search/web <参数>"
operation = parts[0].lower()
param = parts[1].strip() if len(parts) > 1 else ""
if not param:
return f"错误:请提供参数。格式: {operation} <网址或搜索词>"
# ── jina.ai 代理组 ──
if operation == "ai_url":
return self._jina_extract(param)
elif operation == "ai_search":
return self._jina_search(param)
elif operation == "ai_web":
return self._jina_search(param)
# ── AI 直连组 ──
elif operation == "url":
return self._fetch_raw(param)
elif operation == "search":
return self._direct_search(param)
elif operation == "web":
return self._direct_search(param)
else:
return f"不支持的操作 '{operation}'。可用: ai_url/ai_search/ai_web/url/search/web"
except Exception as e:
return f"操作错误: {str(e)}"
# ═══════════════════════════════════════
# jina.ai 代理组
# ═══════════════════════════════════════
def _jina_extract(self, url):
"""jina.ai 提取网页正文"""
jina_url = f"https://r.jina.ai/{url}"
result = self._execute_curl(jina_url)
if result.startswith("错误:"):
return result
return f"[jina 提取] 网页内容 (来自 {url}):\n\n{result}"
def _jina_search(self, query):
"""jina.ai 网络搜索"""
encoded = quote(query, safe='')
jina_url = f"https://s.jina.ai/{encoded}"
result = self._execute_curl(jina_url)
if result.startswith("错误:"):
return result
return f"[jina 搜索] 结果 (搜索词: {query}):\n\n{result}"
# ═══════════════════════════════════════
# AI 直连组
# ═══════════════════════════════════════
def _fetch_raw(self, url):
"""直接 HTTP GET 获取网页原始内容"""
if not url.startswith('http://') and not url.startswith('https://'):
url = 'https://' + url
try:
response = requests.get(
url, timeout=self.timeout, headers=self._HEADERS, allow_redirects=True
)
if response.status_code != 200:
return f"错误:HTTP {response.status_code}"
if not response.encoding or response.encoding.lower() == 'iso-8859-1':
response.encoding = response.apparent_encoding or 'utf-8'
content = response.text.strip()
if not content:
return "错误:未获取到内容"
return f"[直连] 原始网页内容 (来自 {url}):\n\n{content}"
except requests.exceptions.Timeout:
return f"错误:请求超时 (>{self.timeout}秒)"
except requests.exceptions.ConnectionError:
return "错误:网络连接失败"
except Exception as e:
return f"错误:{str(e)}"
def _direct_search(self, query):
"""直接搜索 — 用搜索引擎获取结果页,模型自行解析"""
encoded = quote(query, safe='')
# 用必应搜索,返回结果页 HTML,模型自行提取链接和摘要
search_url = f"https://www.bing.com/search?q={encoded}"
try:
response = requests.get(
search_url, timeout=self.timeout, headers=self._HEADERS, allow_redirects=True
)
if response.status_code != 200:
return f"错误:HTTP {response.status_code}"
if not response.encoding or response.encoding.lower() == 'iso-8859-1':
response.encoding = response.apparent_encoding or 'utf-8'
content = response.text.strip()
if not content:
return "错误:未获取到搜索结果"
return f"[直连] 搜索结果页 (搜索词: {query}):\n\n{content}"
except requests.exceptions.Timeout:
return f"错误:请求超时 (>{self.timeout}秒)"
except requests.exceptions.ConnectionError:
return "错误:网络连接失败"
except Exception as e:
return f"错误:{str(e)}"
# ═══════════════════════════════════════
# 通用请求(jina 用,带重试)
# ═══════════════════════════════════════
def _execute_curl(self, url):
last_error = None
for attempt in range(self.max_retries + 1):
try:
response = requests.get(
url, timeout=self.timeout, headers=self._HEADERS, allow_redirects=True
)
if response.status_code != 200:
return f"错误:HTTP {response.status_code}"
if not response.encoding or response.encoding.lower() == 'iso-8859-1':
response.encoding = response.apparent_encoding or 'utf-8'
content = response.text.strip()
if not content:
return "错误:未获取到内容"
return content
except requests.exceptions.Timeout:
last_error = f"请求超时 (>{self.timeout}秒)"
if attempt < self.max_retries:
time.sleep(1)
except requests.exceptions.ConnectionError:
last_error = "网络连接失败"
if attempt < self.max_retries:
time.sleep(1)
except requests.exceptions.RequestException as e:
last_error = str(e)
break
except Exception as e:
last_error = str(e)
break
return f"错误:{last_error}"