refactor: skill 脚本输出改为结构化 JSON
- query_weather: {location, now: {temp,text}, forecast: [{date,day,night,tempMin,tempMax}]}
- search_web: {query, answer, results: [{title,url,content}]}
- get_current_datetime: {datetime}
- list_scheduled_tasks: {tasks: [{name,description,enabled,command,...}]}
- manage_scheduled_task: {ok, action, name} / {error}
LLM 直接看到结构化数据,不再依赖脚本拼接的中文自然语言
This commit is contained in:
@@ -10,7 +10,7 @@ except: print('full')
|
|||||||
" 2>/dev/null || echo "full")
|
" 2>/dev/null || echo "full")
|
||||||
|
|
||||||
case "$format" in
|
case "$format" in
|
||||||
date) TZ='Asia/Shanghai' date '+%Y-%m-%d' ;;
|
date) TZ='Asia/Shanghai' date '+{"datetime": "%Y-%m-%d"}' ;;
|
||||||
time) TZ='Asia/Shanghai' date '+%H:%M:%S' ;;
|
time) TZ='Asia/Shanghai' date '+{"datetime": "%H:%M:%S"}' ;;
|
||||||
*) TZ='Asia/Shanghai' date '+%Y-%m-%d %H:%M:%S' ;;
|
*) TZ='Asia/Shanghai' date '+{"datetime": "%Y-%m-%d %H:%M:%S"}' ;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -1,27 +1,26 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# 列出定时任务(从 PostgreSQL)
|
# 列出定时任务 → JSON 输出
|
||||||
set -e
|
|
||||||
|
|
||||||
DB_URL="${DATABASE_URL}"
|
DB_URL="${DATABASE_URL}"
|
||||||
if [ -z "$DB_URL" ]; then
|
[ -z "$DB_URL" ] && echo '{"tasks":[],"error":"数据库未配置"}' && exit 0
|
||||||
echo "数据库未配置(DATABASE_URL)"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 使用 psql 查询
|
|
||||||
psql "$DB_URL" -t -A -F '|' -c "
|
psql "$DB_URL" -t -A -F '|' -c "
|
||||||
SELECT name, description, enabled, next_run_at, interval_seconds
|
SELECT jsonb_build_object(
|
||||||
FROM scheduled_tasks
|
'name', name,
|
||||||
ORDER BY name
|
'description', description,
|
||||||
" 2>/dev/null | while IFS='|' read -r name desc enabled next_run interval; do
|
'enabled', enabled,
|
||||||
if [ -n "$name" ]; then
|
'command', command,
|
||||||
echo "- $name${desc:+ ($desc)}"
|
'interval_seconds', interval_seconds,
|
||||||
echo " 启用: $enabled | 间隔: ${interval}s | 下次: ${next_run:-无}"
|
'next_run_at', next_run_at
|
||||||
fi
|
)
|
||||||
done
|
FROM scheduled_tasks ORDER BY name
|
||||||
|
" 2>/dev/null | python3 -c "
|
||||||
# 检查是否有任何任务
|
import sys, json
|
||||||
COUNT=$(psql "$DB_URL" -t -A -c "SELECT count(*) FROM scheduled_tasks" 2>/dev/null || echo "0")
|
tasks = []
|
||||||
if [ "$COUNT" = "0" ] || [ -z "$COUNT" ]; then
|
for line in sys.stdin:
|
||||||
echo "暂无定时任务"
|
line = line.strip()
|
||||||
fi
|
if line:
|
||||||
|
try: tasks.append(json.loads(line))
|
||||||
|
except: pass
|
||||||
|
print(json.dumps({'tasks': tasks}, ensure_ascii=False))
|
||||||
|
"
|
||||||
|
[ -z "$(psql "$DB_URL" -t -A -c "SELECT count(*) FROM scheduled_tasks" 2>/dev/null)" ] && echo '{"tasks":[]}'
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# 管理定时任务
|
# 管理定时任务 → JSON 输出
|
||||||
set -e
|
|
||||||
|
|
||||||
read -r input
|
read -r input
|
||||||
ACTION=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('action',''))" 2>/dev/null)
|
ACTION=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('action',''))" 2>/dev/null)
|
||||||
NAME=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('name',''))" 2>/dev/null)
|
NAME=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('name',''))" 2>/dev/null)
|
||||||
@@ -10,39 +8,32 @@ INTERVAL=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdi
|
|||||||
DESC=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('description',''))" 2>/dev/null)
|
DESC=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('description',''))" 2>/dev/null)
|
||||||
|
|
||||||
DB_URL="${DATABASE_URL}"
|
DB_URL="${DATABASE_URL}"
|
||||||
|
[ -z "$DB_URL" ] && echo '{"error":"数据库未配置"}' && exit 0
|
||||||
if [ -z "$DB_URL" ]; then
|
[ -z "$NAME" ] && echo '{"error":"请提供 name 参数"}' && exit 0
|
||||||
echo "数据库未配置(DATABASE_URL)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$ACTION" in
|
case "$ACTION" in
|
||||||
add)
|
add)
|
||||||
if [ -z "$CMD" ]; then
|
[ -z "$CMD" ] && echo '{"error":"add 需要 command 参数"}' && exit 0
|
||||||
echo "add 操作需要 command 参数"
|
psql "$DB_URL" -c "
|
||||||
exit 1
|
INSERT INTO scheduled_tasks (id, name, description, command, interval_seconds, enabled, next_run_at)
|
||||||
fi
|
VALUES (gen_random_uuid(), '$NAME', '$DESC', '$CMD', $INTERVAL, true, NOW() + interval '$INTERVAL seconds')
|
||||||
psql "$DB_URL" -c "
|
ON CONFLICT (name) DO UPDATE SET command=EXCLUDED.command, interval_seconds=EXCLUDED.interval_seconds, enabled=true, description=EXCLUDED.description
|
||||||
INSERT INTO scheduled_tasks (id, name, description, command, interval_seconds, enabled, next_run_at)
|
" 2>/dev/null && echo '{"ok":true,"action":"add","name":"'"$NAME"'"}' || echo '{"error":"添加失败"}'
|
||||||
VALUES (gen_random_uuid(), '$NAME', '$DESC', '$CMD', $INTERVAL, true, NOW() + interval '$INTERVAL seconds')
|
;;
|
||||||
ON CONFLICT (name) DO UPDATE
|
delete)
|
||||||
SET command = EXCLUDED.command, interval_seconds = EXCLUDED.interval_seconds, enabled = true, description = EXCLUDED.description
|
psql "$DB_URL" -c "DELETE FROM scheduled_tasks WHERE name = '$NAME'" 2>/dev/null
|
||||||
" 2>/dev/null && echo "✅ 已添加/更新定时任务: $NAME" || echo "❌ 添加失败(表 scheduled_tasks 可能未创建)"
|
echo '{"ok":true,"action":"delete","name":"'"$NAME"'"}'
|
||||||
;;
|
;;
|
||||||
delete)
|
enable)
|
||||||
psql "$DB_URL" -c "DELETE FROM scheduled_tasks WHERE name = '$NAME'" 2>/dev/null
|
psql "$DB_URL" -c "UPDATE scheduled_tasks SET enabled = true WHERE name = '$NAME'" 2>/dev/null
|
||||||
echo "✅ 已删除定时任务: $NAME"
|
echo '{"ok":true,"action":"enable","name":"'"$NAME"'"}'
|
||||||
;;
|
;;
|
||||||
enable)
|
disable)
|
||||||
psql "$DB_URL" -c "UPDATE scheduled_tasks SET enabled = true WHERE name = '$NAME'" 2>/dev/null
|
psql "$DB_URL" -c "UPDATE scheduled_tasks SET enabled = false WHERE name = '$NAME'" 2>/dev/null
|
||||||
echo "✅ 已启用定时任务: $NAME"
|
echo '{"ok":true,"action":"disable","name":"'"$NAME"'"}'
|
||||||
;;
|
;;
|
||||||
disable)
|
*)
|
||||||
psql "$DB_URL" -c "UPDATE scheduled_tasks SET enabled = false WHERE name = '$NAME'" 2>/dev/null
|
echo '{"error":"未知操作: '"$ACTION"'(支持: add, delete, enable, disable)"}'
|
||||||
echo "✅ 已禁用定时任务: $NAME"
|
exit 1
|
||||||
;;
|
;;
|
||||||
*)
|
|
||||||
echo "未知操作: $ACTION(支持: add, delete, enable, disable)"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -1,160 +1,53 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# 和风天气查询脚本
|
# 和风天气查询 → 输出 JSON
|
||||||
# 读取 stdin JSON: {"location":"北京","days":3}
|
|
||||||
|
|
||||||
read -r input
|
read -r input
|
||||||
|
LOCATION=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('location',''))" 2>/dev/null)
|
||||||
|
DAYS=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('days',3))" 2>/dev/null)
|
||||||
|
|
||||||
LOCATION=$(echo "$input" | python3 -c "
|
[ -z "$LOCATION" ] && echo '{"error":"请提供 location 参数"}' && exit 0
|
||||||
import sys, json
|
|
||||||
d = json.load(sys.stdin)
|
|
||||||
print(d.get('location', ''))
|
|
||||||
" 2>/dev/null)
|
|
||||||
|
|
||||||
DAYS=$(echo "$input" | python3 -c "
|
|
||||||
import sys, json
|
|
||||||
d = json.load(sys.stdin)
|
|
||||||
print(d.get('days', 3))
|
|
||||||
" 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -z "$LOCATION" ]; then
|
|
||||||
echo "请提供 location 参数"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 从环境变量读取配置
|
|
||||||
API_HOST="${QWEATHER_API_HOST}"
|
API_HOST="${QWEATHER_API_HOST}"
|
||||||
|
KEY_FILE="${QWEATHER_JWT_PRIVATE_KEY_FILE:-qweather/ed25519-private.pem}"
|
||||||
KEY_ID="${QWEATHER_JWT_KEY_ID}"
|
KEY_ID="${QWEATHER_JWT_KEY_ID}"
|
||||||
PROJECT_ID="${QWEATHER_JWT_PROJECT_ID}"
|
PROJECT_ID="${QWEATHER_JWT_PROJECT_ID}"
|
||||||
KEY_FILE="${QWEATHER_JWT_PRIVATE_KEY_FILE:-qweather/ed25519-private.pem}"
|
|
||||||
TTL="${QWEATHER_JWT_TTL_SECONDS:-3600}"
|
TTL="${QWEATHER_JWT_TTL_SECONDS:-3600}"
|
||||||
|
|
||||||
if [ -z "$API_HOST" ]; then
|
NOW=$(date +%s)
|
||||||
echo "请设置 QWEATHER_API_HOST 环境变量"
|
HEADER=$(echo -n '{"alg":"EdDSA","kid":"'"$KEY_ID"'"}' | base64 -w0 | tr '+/' '-_' | tr -d '=')
|
||||||
exit 1
|
PAYLOAD=$(echo -n '{"sub":"'"$PROJECT_ID"'","iat":'$((NOW-30))',"exp":'$((NOW+TTL))'}' | base64 -w0 | tr '+/' '-_' | tr -d '=')
|
||||||
fi
|
INPUT="${HEADER}.${PAYLOAD}"
|
||||||
|
TMP=$(mktemp) && echo -n "$INPUT" > "$TMP"
|
||||||
|
SIG=$(openssl pkeyutl -sign -inkey "$KEY_FILE" -rawin -in "$TMP" 2>/dev/null | base64 -w0 | tr '+/' '-_' | tr -d '=')
|
||||||
|
rm -f "$TMP"
|
||||||
|
JWT="${INPUT}.${SIG}"
|
||||||
|
[ -z "$JWT" ] && JWT="${QWEATHER_JWT}"
|
||||||
|
[ -z "$JWT" ] && echo '{"error":"JWT 生成失败"}' && exit 0
|
||||||
|
|
||||||
# 生成 JWT(使用 python3)
|
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$LOCATION'))")
|
||||||
JWT=$(python3 -c "
|
CITY=$(curl -s --compressed "https://geoapi.qweather.com/v2/city/lookup?location=${ENCODED}" -H "Authorization: Bearer $JWT")
|
||||||
import json, time, base64, struct, sys
|
CITY_ID=$(echo "$CITY" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['location'][0]['id'] if d.get('location') else '')" 2>/dev/null)
|
||||||
from pathlib import Path
|
[ -z "$CITY_ID" ] && echo '{"error":"未找到城市: '"$LOCATION"'"}' && exit 0
|
||||||
|
|
||||||
key_id = '$KEY_ID'
|
EP_DAYS=$([ "$DAYS" -le 3 ] && echo 3 || echo 7)
|
||||||
project_id = '$PROJECT_ID'
|
|
||||||
key_file = '$KEY_FILE'
|
|
||||||
ttl = int('$TTL')
|
|
||||||
|
|
||||||
if not key_id or not project_id:
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Read Ed25519 private key
|
|
||||||
key_path = Path(key_file)
|
|
||||||
if not key_path.exists():
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
key_data = key_path.read_text()
|
|
||||||
|
|
||||||
# Simple base64url
|
|
||||||
def b64url(data):
|
|
||||||
return base64.urlsafe_b64encode(data).rstrip(b'=').decode()
|
|
||||||
|
|
||||||
# Extract raw key bytes from PEM
|
|
||||||
import re
|
|
||||||
pem_match = re.search(r'-----BEGIN PRIVATE KEY-----\\s*(.*?)\\s*-----END PRIVATE KEY-----', key_data, re.DOTALL)
|
|
||||||
if not pem_match:
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
der = base64.b64decode(pem_match.group(1))
|
|
||||||
# PKCS8 Ed25519 private key: last 32 bytes are the seed
|
|
||||||
seed = der[-32:]
|
|
||||||
|
|
||||||
from hashlib import sha512
|
|
||||||
h = sha512(seed).digest()
|
|
||||||
a = bytearray(h[:32])
|
|
||||||
a[0] &= 248
|
|
||||||
a[31] &= 127
|
|
||||||
a[31] |= 64
|
|
||||||
|
|
||||||
# Sign using RFC 8032
|
|
||||||
def ed25519_sign(message, seed_bytes):
|
|
||||||
h = sha512(seed_bytes).digest()
|
|
||||||
a = bytearray(h[:32])
|
|
||||||
a[0] &= 248
|
|
||||||
a[31] &= 127
|
|
||||||
a[31] |= 64
|
|
||||||
r = sha512(bytes(a[32:]) + message).digest()
|
|
||||||
R = _sc_reduce(r)
|
|
||||||
R_bytes = _sc_mul_base(R)
|
|
||||||
S = _sc_add(R, _sc_mul(_sc_reduce(sha512(bytes(R_bytes) + bytes(a) + message).digest()), _sc_reduce(bytes(a))))
|
|
||||||
return bytes(R_bytes) + _sc_encode(S)
|
|
||||||
|
|
||||||
# Skipping full impl - use openssl instead
|
|
||||||
print('')
|
|
||||||
" 2>/dev/null)
|
|
||||||
|
|
||||||
# If python3 JWT failed, try openssl
|
|
||||||
if [ -z "$JWT" ]; then
|
|
||||||
NOW=$(date +%s)
|
|
||||||
IAT=$((NOW - 30))
|
|
||||||
EXP=$((NOW + TTL))
|
|
||||||
|
|
||||||
HEADER=$(echo -n '{"alg":"EdDSA","kid":"'"$KEY_ID"'"}' | base64 -w0 | tr '+/' '-_' | tr -d '=')
|
|
||||||
PAYLOAD=$(echo -n '{"sub":"'"$PROJECT_ID"'","iat":'"$IAT"',"exp":'"$EXP"'}' | base64 -w0 | tr '+/' '-_' | tr -d '=')
|
|
||||||
SIGNING_INPUT="${HEADER}.${PAYLOAD}"
|
|
||||||
|
|
||||||
# Try openssl pkeyutl (OpenSSL >= 3.0, needs temp file for -rawin)
|
|
||||||
TMPFILE=$(mktemp)
|
|
||||||
echo -n "$SIGNING_INPUT" > "$TMPFILE"
|
|
||||||
SIGNATURE=$(openssl pkeyutl -sign -inkey "$KEY_FILE" -rawin -in "$TMPFILE" 2>/dev/null | base64 -w0 | tr '+/' '-_' | tr -d '=') || true
|
|
||||||
rm -f "$TMPFILE"
|
|
||||||
if [ -z "$SIGNATURE" ]; then
|
|
||||||
# Fallback: try older openssl
|
|
||||||
SIGNATURE=$(echo -n "$SIGNING_INPUT" | openssl dgst -sign "$KEY_FILE" -keyform PEM -sha512 2>/dev/null | base64 -w0 | tr '+/' '-_' | tr -d '=') || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$SIGNATURE" ]; then
|
|
||||||
JWT="${SIGNING_INPUT}.${SIGNATURE}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Try env var JWT as fallback
|
|
||||||
if [ -z "$JWT" ]; then
|
|
||||||
JWT="${QWEATHER_JWT}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$JWT" ]; then
|
|
||||||
echo "无法生成 QWeather JWT,请检查 QWEATHER_JWT_KEY_ID/QWEATHER_JWT_PROJECT_ID/QWEATHER_JWT_PRIVATE_KEY_FILE 配置"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 查询城市 ID
|
|
||||||
CITY_RESP=$(curl -s --compressed "https://geoapi.qweather.com/v2/city/lookup?location=$(echo -n "$LOCATION" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))")" -H "Authorization: Bearer $JWT")
|
|
||||||
CITY_ID=$(echo "$CITY_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['location'][0]['id'] if d.get('location') else '')" 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -z "$CITY_ID" ]; then
|
|
||||||
echo "未找到城市: $LOCATION"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 查询实况天气
|
|
||||||
NOW_RESP=$(curl -s --compressed "https://${API_HOST}/v7/weather/now?location=${CITY_ID}&lang=zh&unit=m" -H "Authorization: Bearer $JWT")
|
NOW_RESP=$(curl -s --compressed "https://${API_HOST}/v7/weather/now?location=${CITY_ID}&lang=zh&unit=m" -H "Authorization: Bearer $JWT")
|
||||||
NOW_TEMP=$(echo "$NOW_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(f\"{d['now']['temp']}°C, {d['now']['text']}\")" 2>/dev/null)
|
DAILY_RESP=$(curl -s --compressed "https://${API_HOST}/v7/weather/${EP_DAYS}d?location=${CITY_ID}&lang=zh&unit=m" -H "Authorization: Bearer $JWT")
|
||||||
|
|
||||||
# 查询预报(最多7天)
|
export NOW_RESP DAILY_RESP LOCATION DAYS
|
||||||
ENDPOINT_DAYS=$DAYS
|
python3 -c "
|
||||||
[ "$DAYS" -gt 3 ] && ENDPOINT_DAYS=7
|
import os, json
|
||||||
[ "$DAYS" -le 3 ] && ENDPOINT_DAYS=3
|
now = json.loads(os.environ['NOW_RESP'])
|
||||||
|
daily = json.loads(os.environ['DAILY_RESP'])
|
||||||
DAILY_RESP=$(curl -s --compressed "https://${API_HOST}/v7/weather/${ENDPOINT_DAYS}d?location=${CITY_ID}&lang=zh&unit=m" -H "Authorization: Bearer $JWT")
|
days = daily.get('daily', [])[:int(os.environ['DAYS'])]
|
||||||
FORECAST=$(echo "$DAILY_RESP" | python3 -c "
|
result = {
|
||||||
import sys, json
|
'location': os.environ['LOCATION'],
|
||||||
d = json.load(sys.stdin)
|
'now': {
|
||||||
days = d.get('daily', [])[:int('$DAYS')]
|
'temp': now['now']['temp'],
|
||||||
lines = ['未来天气:']
|
'text': now['now']['text'],
|
||||||
for day in days:
|
},
|
||||||
lines.append(f\" {day['fxDate']}: {day['textDay']} {day['tempMin']}~{day['tempMax']}°C\")
|
'forecast': [
|
||||||
print('\\n'.join(lines))
|
{'date': d['fxDate'], 'day': d['textDay'], 'night': d.get('textNight',''), 'tempMin': d['tempMin'], 'tempMax': d['tempMax']}
|
||||||
" 2>/dev/null)
|
for d in days
|
||||||
|
]
|
||||||
echo "${LOCATION}天气: 当前 ${NOW_TEMP}"
|
}
|
||||||
echo ""
|
print(json.dumps(result, ensure_ascii=False))
|
||||||
echo "$FORECAST"
|
" 2>/dev/null || echo '{"error":"解析天气数据失败"}'
|
||||||
|
|||||||
@@ -1,54 +1,29 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Tavily 搜索
|
# Tavily 搜索 → JSON 输出
|
||||||
|
|
||||||
read -r input
|
read -r input
|
||||||
QUERY=$(echo "$input" | python3 -c "
|
QUERY=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('query',''))" 2>/dev/null)
|
||||||
import sys, json
|
COUNT=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('count',5))" 2>/dev/null)
|
||||||
d = json.load(sys.stdin)
|
|
||||||
print(d.get('query', ''))
|
|
||||||
" 2>/dev/null)
|
|
||||||
|
|
||||||
COUNT=$(echo "$input" | python3 -c "
|
[ -z "$QUERY" ] && echo '{"error":"请提供 query 参数"}' && exit 0
|
||||||
import sys, json
|
|
||||||
d = json.load(sys.stdin)
|
|
||||||
print(d.get('count', 5))
|
|
||||||
" 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -z "$QUERY" ]; then
|
|
||||||
echo "请提供 query 参数"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
API_KEY="${TAVILY_API_KEY}"
|
API_KEY="${TAVILY_API_KEY}"
|
||||||
if [ -z "$API_KEY" ]; then
|
[ -z "$API_KEY" ] && echo '{"error":"请设置 TAVILY_API_KEY"}' && exit 0
|
||||||
echo "请设置 TAVILY_API_KEY 环境变量"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
RESP=$(curl -s -X POST "https://api.tavily.com/search" \
|
RESP=$(curl -s -X POST "https://api.tavily.com/search" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"api_key\":\"$API_KEY\",\"query\":\"$QUERY\",\"max_results\":$COUNT,\"include_answer\":true}")
|
-d "{\"api_key\":\"$API_KEY\",\"query\":\"$QUERY\",\"max_results\":$COUNT,\"include_answer\":true}")
|
||||||
|
|
||||||
# 解析并输出
|
export RESP QUERY COUNT
|
||||||
ANSWER=$(echo "$RESP" | python3 -c "
|
python3 -c "
|
||||||
import sys, json
|
import os, json
|
||||||
d = json.load(sys.stdin)
|
d = json.loads(os.environ['RESP'])
|
||||||
answer = d.get('answer', '')
|
result = {
|
||||||
results = d.get('results', [])
|
'query': os.environ['QUERY'],
|
||||||
lines = []
|
'answer': d.get('answer', ''),
|
||||||
if answer:
|
'results': [
|
||||||
lines.append(f'总结: {answer}')
|
{'title': r.get('title', ''), 'url': r.get('url', ''), 'content': r.get('content', '')[:300]}
|
||||||
lines.append('')
|
for r in d.get('results', [])[:int(os.environ['COUNT'])]
|
||||||
for i, r in enumerate(results[:$COUNT], 1):
|
]
|
||||||
title = r.get('title', '无标题')
|
}
|
||||||
url = r.get('url', '')
|
print(json.dumps(result, ensure_ascii=False))
|
||||||
content = r.get('content', '')[:200]
|
" 2>/dev/null || echo '{"error":"搜索请求失败"}'
|
||||||
lines.append(f'{i}. {title}')
|
|
||||||
lines.append(f' {url}')
|
|
||||||
if content:
|
|
||||||
lines.append(f' {content}')
|
|
||||||
lines.append('')
|
|
||||||
print('\\n'.join(lines))
|
|
||||||
" 2>/dev/null)
|
|
||||||
|
|
||||||
echo "$ANSWER"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user