Files
HomeAgent/assets/skills/knowledge-base/scripts/kb_tree.sh
JianFeeeee fc5563bc75 docs(kbtree): 补客户端封装脚本 + 本机部署交接说明
SKILL.md 里原本只有 curl 示例,agent 用起来要自己拼 URL 与鉴权头。
补 scripts/kb_tree.sh(照 dify-ops 的做法把脚本随 skill 分发):

- 子命令 tree/categories/counts/search,选项 -q/-c/-d/-i/-l
- token 读取顺序:KB_TOKEN 环境变量 → config/config.json。
  **故意不接受命令行传 token**(会进 shell 历史与 ps 输出)
- 预检端口:不通时直接说明「服务未上线」并给出上线步骤,
  而不是抛 curl: (7) Connection refused 让用户自己猜
- 错误翻译成人话:401 → 令牌无效;404 → 附上服务端返回的现有分类
  (用 curl -w 而非 -f,否则 404 响应体里最有用的那份清单会被丢掉)
- 退出码:0 成功 / 2 参数 / 3 令牌 / 4 分类不存在 / 5 HTTP / 7 连不上 / 8 请求失败

DEPLOY.md 是给部署方的交接单:本机 kbtree 代码已合入 main 且 skill/token
已就位,但**运行中的二进制里没有 kbtree**(14:35 有人换过一版二进制),
故替换与重启留给部署方。含备份/替换/验证步骤、回滚方式、端口与 token 说明。

token 与 config.json **不入库**(config/ 目录留空),安装时由部署方从
配置库 config_kbtree 表读取后写入本机。
2026-09-26 14:43:46 +08:00

155 lines
5.6 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# kbtree 客户端:按分类树检索 HomeAgent 知识库。
#
# 由 knowledge-base skill 调用(agent 读 SKILL.md 后按需调本脚本)。
# 也可人工使用:
# kb_tree.sh # 整棵树
# kb_tree.sh -c tech -d 1 # tech 子树,只看一层
# kb_tree.sh -q goroutine -c tech # 在 tech 子树内检索
# kb_tree.sh -l # 列分类
#
# token 读取顺序:环境变量 KB_TOKEN > 本目录 config.json > 报错。
# 故意不放命令行参数:token 会进 shell 历史与 ps 输出。
set -euo pipefail
BASE="${KB_BASE:-http://127.0.0.1:9892}"
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 读 token
# 帮助:-h/--help 必须在解析命令之前判,否则会被当成未知命令
for a in "$@"; do
case "$a" in
-h|--help)
echo "用法:"
echo " kb_tree.sh [tree|categories|counts|search] [选项]"
echo "选项:"
echo " -q 关键词 检索词(给了 q 就走 search)"
echo " -c 分类 限定分类子树(前缀匹配),如 tech / tech/go"
echo " -d 层数 树展开层数,1=只看第一层(懒加载)"
echo " -i 0|1 是否带条目详情,默认 1"
echo " -l 条数 search 的返回条数,默认 10"
echo "环境变量: KB_TOKEN(也可放同目录 config/config.json)"
exit 0 ;;
esac
done
if [[ -z "${KB_TOKEN:-}" ]]; then
CFG="$HERE/../config.json"
if [[ -f "$CFG" ]]; then
KB_TOKEN="$(python3 -c 'import json,sys;print(json.load(open(sys.argv[1])).get("token",""))' "$CFG" 2>/dev/null || true)"
fi
fi
if [[ -z "${KB_TOKEN:-}" ]]; then
cat >&2 <<'EOF'
kbtree: 未找到访问令牌。
取 token 的办法(其一):
1. 若 homed 启动日志里有「token 未配置,本次随机生成」,那行日志附近有 token;
2. 若已配置过固定 token,从配置库读:
sqlite3 <data_dir>/config.db "SELECT value FROM config_kbtree WHERE key='token'"
3. 直接设置:export KB_TOKEN=<token>
token 未配置时服务会在启动时随机生成,进程重启即失效。
EOF
exit 2
fi
auth=(-H "X-API-Key: ${KB_TOKEN}")
cmd="${1:-tree}"; shift || true
query=""; category=""; depth=""; items=""; limit=""
while getopts "q:c:d:i:l:h" opt 2>/dev/null; do
case "$opt" in
q) query="$OPTARG" ;;
c) category="$OPTARG" ;;
d) depth="$OPTARG" ;;
i) items="$OPTARG" ;;
l) limit="$OPTARG" ;;
h) echo "用法: kb_tree.sh [tree|categories|counts|search] -q 关键词 -c 分类 -d 层数 -i 0|1 -l 条数"; exit 0 ;;
esac
done
# 预检:端口通不通。不通就直说"服务未上线",
# 而不是让用户看 curl: (7) Connection refused 后自行猜测。
HOSTPORT="${BASE#http://}"; HOSTPORT="${HOSTPORT%%/*}"
if ! (exec 3<>"/dev/tcp/${HOSTPORT%%:*}/${HOSTPORT##*:}") 2>/dev/null; then
cat >&2 <<EOF
kbtree: 连不上 $BASE(端口未监听)。
最常见原因——**服务还没上线**。kbtree 是 HomeAgent 的内置插件,
需要二进制里含它才会启动。若 homed 是在合入该插件之前构建/部署的,
就一直没有这个服务。上线后验证:
systemctl show homeagent -p MainPID --value
strings /usr/local/bin/homed | grep -c kbtree # 0 = 二进制里没有
本机上线步骤(会重启守护进程):
sqlite3 <data_dir>/config.db ".backup '<data_dir>/config.db.bak-\$(date +%Y%m%d-%H%M%S)'"
install -m 0755 <新二进制> /usr/local/bin/homed # 勿用 cp:会写坏运行中进程的映像
systemctl restart homeagent
journalctl -u homeagent -f | grep kbtree
EOF
exit 7
fi
enc() { python3 -c 'import sys,urllib.parse;print(urllib.parse.quote(sys.argv[1]))' "$1"; }
# kb_call 发请求并把错误翻译成人话。
# 不用 curl -f:它只吐 "curl: (22) 404",把服务端给的有用信息(404 会附现有分类)
# 全丢了 —— 而那恰恰是排查时最需要的。
kb_call() {
local url="$1" raw code
raw="$(curl -sS -w $'\n%{http_code}' "${auth[@]}" "$url" 2>&1)" || {
echo "kbtree: 请求失败: ${raw}" >&2; exit 8
}
code="$(printf '%s' "$raw" | tail -n1)"
body="$(printf '%s' "$raw" | sed '$d')"
case "$code" in
2*) printf '%s' "$body" | python3 -m json.tool; return 0 ;;
401) echo "kbtree: 令牌无效(401)。检查 config/config.json 或 KB_TOKEN 是否与 config_kbtree 表一致。" >&2; exit 3 ;;
404) echo "kbtree: 分类不存在(404)。服务端返回的现有分类:" >&2
printf '%s' "$body" | KB_BODY="$body" python3 -c '
import json, os, sys
try:
d = json.loads(os.environ.get("KB_BODY",""))
print(" " + ", ".join(d.get("categories", [])), file=sys.stderr)
except Exception:
print(" (无法解析响应体)", file=sys.stderr)
'
exit 4 ;;
*) echo "kbtree: HTTP $code" >&2; printf '%s\n' "$body" >&2; exit 5 ;;
esac
}
case "$cmd" in
tree|categories|counts|search) ;;
*) echo "未知命令: $cmd" >&2; exit 2 ;;
esac
if [[ "$cmd" == "categories" ]]; then
kb_call "$BASE/categories"
exit 0
fi
if [[ "$cmd" == "counts" ]]; then
kb_call "$BASE/counts"
exit 0
fi
if [[ "$cmd" == "search" || -n "$query" ]]; then
[[ -n "$query" ]] || { echo "search 需要 -q 关键词" >&2; exit 2; }
url="$BASE/search?q=$(enc "$query")"
[[ -n "$category" ]] && url="$url&category=$(enc "$category")"
[[ -n "$limit" ]] && url="$url&limit=$limit"
kb_call "$url"
exit 0
fi
# 默认:树
url="$BASE/tree"
[[ -n "$category" ]] && url="$url?category=$(enc "$category")"
q=""
[[ -n "$depth" ]] && q="depth=$depth"
[[ -n "$items" ]] && q="${q:+$q&}items=$items"
[[ -n "$q" ]] && url="$url?$q"
kb_call "$url"