| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- #!/usr/bin/env python3
- """
- KnowHub CLI - 知识管理命令行工具
- 使用方法:
- python -m knowhub.skill.cli search "查询内容"
- python -m knowhub.skill.cli save --task "任务" --content "内容" --types strategy
- python -m knowhub.skill.cli list --limit 10
- """
- import os
- import sys
- import json
- import argparse
- from pathlib import Path
- try:
- import httpx
- except ImportError:
- print("错误: 需要安装 httpx 库")
- print("运行: pip install httpx")
- sys.exit(1)
- # KnowHub API 默认地址(CLI 需要在开头声明,方便用户查看和修改)
- DEFAULT_API_URL = "http://43.106.118.91:9999"
- def get_api_base() -> str:
- """获取 API 地址"""
- return os.getenv("KNOWHUB_API", DEFAULT_API_URL)
- def search_knowledge(args):
- """搜索知识"""
- url = f"{get_api_base()}/api/knowledge/search"
- params = {
- "q": args.query,
- "top_k": args.top_k,
- "min_score": args.min_score,
- }
- if args.types:
- params["types"] = args.types
- try:
- response = httpx.get(url, params=params, timeout=30.0)
- response.raise_for_status()
- data = response.json()
- if data["count"] == 0:
- print("未找到相关知识")
- return
- print(f"找到 {data['count']} 条知识:\n")
- for i, item in enumerate(data["results"], 1):
- print(f"[{i}] {item['task']}")
- print(f" ID: {item['id']}")
- eval_data = item.get("eval", {})
- print(f" 评分: {eval_data.get('score', 3)} | 质量分: {item.get('quality_score', 'N/A')}")
- print(f" 类型: {', '.join(item.get('types', []))}")
- print(f" 内容: {item['content'][:100]}...")
- print()
- except httpx.HTTPError as e:
- print(f"请求失败: {e}")
- sys.exit(1)
- def save_knowledge(args):
- """保存知识"""
- url = f"{get_api_base()}/api/knowledge"
- data = {
- "message_id": args.message_id or f"cli-{os.getpid()}",
- "types": args.types.split(",") if args.types else ["strategy"],
- "task": args.task,
- "tags": json.loads(args.tags) if args.tags else {},
- "scopes": args.scopes.split(",") if args.scopes else ["org:cybertogether"],
- "owner": args.owner or "agent:cli",
- "content": args.content,
- "source": {
- "name": args.source_name or "cli",
- "category": args.source_category or "exp",
- "urls": args.urls.split(",") if args.urls else [],
- "agent_id": args.agent_id or "cli",
- "submitted_by": args.submitted_by or "cli-user",
- },
- "eval": {
- "score": args.score,
- "helpful": 1,
- "harmful": 0,
- "confidence": 0.5,
- }
- }
- try:
- response = httpx.post(url, json=data, timeout=30.0)
- response.raise_for_status()
- result = response.json()
- print(f"✅ 知识已保存: {result['knowledge_id']}")
- except httpx.HTTPError as e:
- print(f"保存失败: {e}")
- sys.exit(1)
- def update_knowledge(args):
- """更新知识"""
- url = f"{get_api_base()}/api/knowledge/{args.id}"
- data = {}
- if args.score:
- data["update_score"] = args.score
- if args.helpful_case:
- data["add_helpful_case"] = args.helpful_case
- if args.harmful_case:
- data["add_harmful_case"] = args.harmful_case
- if args.evolve_feedback:
- data["evolve_feedback"] = args.evolve_feedback
- if not data:
- print("错误: 至少需要提供一个更新参数")
- sys.exit(1)
- try:
- response = httpx.put(url, json=data, timeout=30.0)
- response.raise_for_status()
- print(f"✅ 知识已更新: {args.id}")
- except httpx.HTTPError as e:
- print(f"更新失败: {e}")
- sys.exit(1)
- def batch_update_knowledge(args):
- """批量更新知识"""
- url = f"{get_api_base()}/api/knowledge/batch_update"
- # 从文件读取反馈列表
- if args.file:
- with open(args.file, 'r') as f:
- feedback_list = json.load(f)
- else:
- print("错误: 需要提供 --file 参数")
- sys.exit(1)
- data = {"feedback_list": feedback_list}
- try:
- response = httpx.post(url, json=data, timeout=60.0)
- response.raise_for_status()
- result = response.json()
- print(f"✅ 批量更新完成: {result['updated']} 条知识")
- except httpx.HTTPError as e:
- print(f"批量更新失败: {e}")
- sys.exit(1)
- def list_knowledge(args):
- """列出知识"""
- url = f"{get_api_base()}/api/knowledge"
- params = {"limit": args.limit}
- if args.types:
- params["types"] = args.types
- if args.scopes:
- params["scopes"] = args.scopes
- try:
- response = httpx.get(url, params=params, timeout=30.0)
- response.raise_for_status()
- data = response.json()
- if data["count"] == 0:
- print("知识库为空")
- return
- print(f"共 {data['count']} 条知识:\n")
- for i, item in enumerate(data["results"], 1):
- print(f"[{i}] {item['task']}")
- print(f" ID: {item['id']}")
- eval_data = item.get("eval", {})
- print(f" 评分: {eval_data.get('score', 3)} | Helpful: {eval_data.get('helpful', 0)} | Harmful: {eval_data.get('harmful', 0)}")
- print(f" 类型: {', '.join(item.get('types', []))}")
- print(f" 所有者: {item.get('owner', 'N/A')}")
- print()
- except httpx.HTTPError as e:
- print(f"请求失败: {e}")
- sys.exit(1)
- def slim_knowledge(args):
- """知识瘦身"""
- url = f"{get_api_base()}/api/knowledge/slim"
- params = {"model": args.model}
- try:
- print("正在执行知识瘦身,这可能需要一些时间...")
- response = httpx.post(url, params=params, timeout=120.0)
- response.raise_for_status()
- result = response.json()
- print(f"✅ 瘦身完成: {result['before']} → {result['after']} 条知识")
- if result.get("report"):
- print(f" {result['report']}")
- except httpx.HTTPError as e:
- print(f"瘦身失败: {e}")
- sys.exit(1)
- def main():
- parser = argparse.ArgumentParser(description="KnowHub CLI - 知识管理工具")
- subparsers = parser.add_subparsers(dest="command", help="可用命令")
- # search 命令
- search_parser = subparsers.add_parser("search", help="搜索知识")
- search_parser.add_argument("query", help="查询文本")
- search_parser.add_argument("--top-k", type=int, default=5, help="返回结果数量")
- search_parser.add_argument("--min-score", type=int, default=3, help="最低评分")
- search_parser.add_argument("--types", help="类型过滤(逗号分隔)")
- # save 命令
- save_parser = subparsers.add_parser("save", help="保存知识")
- save_parser.add_argument("--task", required=True, help="任务描述")
- save_parser.add_argument("--content", required=True, help="知识内容")
- save_parser.add_argument("--types", default="strategy", help="类型(逗号分隔)")
- save_parser.add_argument("--tags", help="标签(JSON 格式)")
- save_parser.add_argument("--scopes", help="可见范围(逗号分隔)")
- save_parser.add_argument("--owner", help="所有者")
- save_parser.add_argument("--source-name", help="来源名称")
- save_parser.add_argument("--source-category", help="来源类别")
- save_parser.add_argument("--urls", help="相关 URL(逗号分隔)")
- save_parser.add_argument("--agent-id", help="Agent ID")
- save_parser.add_argument("--submitted-by", help="提交者")
- save_parser.add_argument("--message-id", help="消息 ID")
- save_parser.add_argument("--score", type=int, default=3, help="评分 (1-5)")
- # update 命令
- update_parser = subparsers.add_parser("update", help="更新知识")
- update_parser.add_argument("id", help="知识 ID")
- update_parser.add_argument("--score", type=int, help="更新评分")
- update_parser.add_argument("--helpful-case", help="添加有效案例")
- update_parser.add_argument("--harmful-case", help="添加有害案例")
- update_parser.add_argument("--evolve-feedback", help="知识进化反馈")
- # batch-update 命令
- batch_parser = subparsers.add_parser("batch-update", help="批量更新知识")
- batch_parser.add_argument("--file", required=True, help="反馈列表 JSON 文件")
- # list 命令
- list_parser = subparsers.add_parser("list", help="列出知识")
- list_parser.add_argument("--limit", type=int, default=10, help="返回数量")
- list_parser.add_argument("--types", help="类型过滤")
- list_parser.add_argument("--scopes", help="范围过滤")
- # slim 命令
- slim_parser = subparsers.add_parser("slim", help="知识瘦身")
- slim_parser.add_argument("--model", default="google/gemini-2.0-flash-001", help="使用的模型")
- args = parser.parse_args()
- if not args.command:
- parser.print_help()
- sys.exit(1)
- # 执行命令
- if args.command == "search":
- search_knowledge(args)
- elif args.command == "save":
- save_knowledge(args)
- elif args.command == "update":
- update_knowledge(args)
- elif args.command == "batch-update":
- batch_update_knowledge(args)
- elif args.command == "list":
- list_knowledge(args)
- elif args.command == "slim":
- slim_knowledge(args)
- if __name__ == "__main__":
- main()
|