#!/usr/bin/env python3 """ AgentFrame CLI — 命令行工具 ============================ 用法: agentframe serve [--port 8090] [--config config.json] agentframe ingest "文本" [--tags a,b] [--session ] [--state ] agentframe ask "问题" [--no-chat] [--hands] [--state ] agentframe stats [--state ] agentframe forget [--threshold 0.15] [--state ] agentframe config [--output config.json] agentframe demo """ import argparse import os import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from agentframe.config import AgentFrameConfig from agentframe.core.engine import ContextEngine DEFAULT_STATE = "/var/lib/agentframe/cli.json" def load_engine(config_path: str = None, state_path: str = None) -> ContextEngine: config = AgentFrameConfig.from_file(config_path) if config_path else AgentFrameConfig.from_env() eng = ContextEngine(config) sp = state_path or DEFAULT_STATE if os.path.exists(sp): eng.load(sp) return eng def cmd_serve(args): from agentframe.api.server import AgentFrameAPI config = AgentFrameConfig.from_file(args.config) if args.config else AgentFrameConfig.from_env() api = AgentFrameAPI(config) print(f"🚀 AgentFrame API — http://{config.api.host}:{args.port or config.api.port}") api.app.run(host=config.api.host, port=args.port or config.api.port, debug=config.api.debug) def cmd_ingest(args): eng = load_engine(args.config, args.state) tags = args.tags.split(",") if args.tags else [] cid = eng.ingest(args.text, tags) sp = args.state or DEFAULT_STATE os.makedirs(os.path.dirname(sp), exist_ok=True) eng.save(sp) print(f"✅ 摄入 chunk_{cid}: {args.text[:50]}...") print(f" 标签: {tags} | 已存: {sp}") def cmd_ask(args): eng = load_engine(args.config, args.state) if args.hands: result = eng.ask_with_hands(args.query) else: result = eng.ask(args.query, chat=not args.no_chat) print(f"\n🧠 检索到 {len(result.retrieved)} 块:") for cid, prev in result.retrieved: print(f" chunk_{cid}: {prev}...") if result.answer: print(f"\n💬 回答:\n{result.answer}") sp = args.state or DEFAULT_STATE eng.save(sp) def cmd_stats(args): eng = load_engine(args.config, args.state) for k, v in eng.stats().items(): print(f" {k}: {v}") def cmd_forget(args): eng = load_engine(args.config, args.state) victims = eng.forget(args.threshold) sp = args.state or DEFAULT_STATE eng.save(sp) print(f"🧹 遗忘 {len(victims)} 块: {victims}") print(f" 剩余: {len(eng.agent.chunk_meta)} 块") def cmd_config(args): config = AgentFrameConfig.from_env() out = args.output or "agentframe.config.json" config.save(out) print(f"✅ 配置已写入: {out}") print(f" LLM: {config.llm.provider} / {config.llm.model}") print(f" Memory: {config.memory.n_layers}层 INT{config.memory.quant_bits} top-k={config.memory.top_k}") print(f" API: {config.api.host}:{config.api.port}") def cmd_demo(args): """离线演示: 摄入 → 查询 → 遗忘 (无 API key 也可跑)""" config = AgentFrameConfig.from_env() config.llm.provider = "mock" eng = ContextEngine(config) print("=" * 56) print("AgentFrame 离线演示 (Mock LLM)") print("=" * 56) chunks = [ {"text": "KV 缓存 270KB/token 展开存储", "tags": ["data"]}, {"text": "吸收式 MLA 缓存 576 维潜在向量", "tags": ["method"]}, {"text": "L40S 实测 35.6x 压缩", "tags": ["data", "result"]}, {"text": "注意力分数不等于任务重要性", "tags": ["agent"]}, ] for c in chunks: cid = eng.ingest(c["text"], c["tags"]) print(f" 📥 摄入 chunk_{cid}: {c['text']}") print(f"\n [存储] {eng.stats()['layers']}") print(f" [压缩] {eng.stats()['bytes_per_token']} B/token") r = eng.ask("KV压缩能到多少倍?", chat=True) print(f"\n 🧠 检索: {r.retrieved}") print(f" 💬 回答: {r.answer}") v = eng.forget(0.3) print(f"\n 🧹 遗忘: {len(v)} 块 → 剩余 {len(eng.agent.chunk_meta)} 块") print("\n✅ 演示完成") def main(): p = argparse.ArgumentParser(prog="agentframe", description="AgentFrame 上下文保持框架") sub = p.add_subparsers(dest="cmd") pserve = sub.add_parser("serve", help="启动 API 服务") pserve.add_argument("--port", type=int, default=None) pserve.add_argument("--config", default=None) pi = sub.add_parser("ingest", help="摄入知识") pi.add_argument("text") pi.add_argument("--tags", default=None) pi.add_argument("--config", default=None) pi.add_argument("--state", default=None) pa = sub.add_parser("ask", help="查询") pa.add_argument("query") pa.add_argument("--no-chat", action="store_true") pa.add_argument("--hands", action="store_true") pa.add_argument("--config", default=None) pa.add_argument("--state", default=None) pst = sub.add_parser("stats", help="状态") pst.add_argument("--config", default=None) pst.add_argument("--state", default=None) pf = sub.add_parser("forget", help="遗忘") pf.add_argument("--threshold", type=float, default=0.15) pf.add_argument("--config", default=None) pf.add_argument("--state", default=None) pc = sub.add_parser("config", help="生成配置") pc.add_argument("--output", default=None) sub.add_parser("demo", help="离线演示") args = p.parse_args() if args.cmd == "serve": cmd_serve(args) elif args.cmd == "ingest": cmd_ingest(args) elif args.cmd == "ask": cmd_ask(args) elif args.cmd == "stats": cmd_stats(args) elif args.cmd == "forget": cmd_forget(args) elif args.cmd == "config": cmd_config(args) elif args.cmd == "demo": cmd_demo(args) else: p.print_help() if __name__ == "__main__": main()