# 021-chat-cli **Repository Path**: henryqi91/021-chat-cli ## Basic Information - **Project Name**: 021-chat-cli - **Description**: 021 model harness trial - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-03 - **Last Updated**: 2026-07-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 021 Agent Harness 基于 021 大语言模型的科学化 Agent Harness / 运维 AI CLI 工具。它把每次交互视为可审计、可复现的实验:先提出假设,再通过只读观察与受控工具调用收集证据,最后输出带置信度和局限性的结论。 > 项目已完成 P0~P3 四阶段架构改造,在原有实验式 ReAct 内核之上,融合了 Skill 元数据、多模型注册、多 Agent 编排、两阶段记忆、并发流控、细粒度权限与 Hook、断路器容错、配置热加载等能力。 ## 核心特性 ### 基础内核 - **实验式 ReAct 循环**:假设 → 观察 → 工具执行 → 证据反馈 → 结论 - **证据保留管理**:LLM 驱动的自动压缩,支持 128K 长上下文对话 - **混合思考模式**:Rich CLI 支持 `/think` 和 `/no_think` 切换,控制模型是否展示推理过程 - **24 个内置工具**:shell、web、file、诊断、等待、Kubernetes、Prometheus、网络工具 - **风险分级工具系统**:L0/L1/L2 三级风险控制与审批策略 - **双界面模式**:Textual TUI(默认)+ Rich CLI - **安全沙箱**:路径验证、循环检测、工具风险管控 ### Skill 与提示工程 - **Skill 元数据系统**:Markdown frontmatter 定义 Skill(`name` / `description` / `triggers` / `model_preference` / `allowed_tools`) - **多源 Skill 加载**:`builtin` / `directory` / `remote` 三种来源 + 优先级合并 - **Progressive Disclosure**:Skill 按需加载,触发词匹配后再注入详情 - **双层提示工程**:PromptManager 模板 + 角色分离,支持 `prompts/` 目录自定义 ### 多模型与多 Agent - **多模型注册表**:`instruct` / `reasoning` / `fast` 逻辑角色映射到实际模型 - **Agent 注册表 + 多 Agent 编排**:AgentRegistry + PI-Team / Handoff / Parallel Replication 模式 - **并行子 Agent + 置信度评分**:TaskDispatcher 多副本执行 + 结果聚合 ### 记忆与容错 - **两阶段记忆管道**:短期(会话内)+ 长期(跨会话)记忆 - **记忆持久化与索引**:SQLite 存储 + 向量/关键词检索 - **断路器 + 超时保护**:CircuitBreaker 三态自动恢复 - **工具级并发 + 自适应流控**:FlowController 并发窗口 + 动态调节 ### 权限、Hook 与配置 - **细粒度权限 + PreToolUse Hook**:白名单/黑名单 + Python callable 拦截 - **Hook 脚本系统**:Python/Bash 脚本作为外部工具拦截器(subprocess 隔离) - **项目级配置**:`.021-project.json` 定义项目专属设置 - **完整配置文件**:`~/.021-agent/config.json` 支持所有模型和 Agent 参数 - **配置热加载**:运行时自动检测配置变更并生效 ## 项目结构 ``` 021-agent-cli/ ├── src/ │ ├── main.py # CLI 入口与参数解析 │ ├── agent.py # 核心 Agent 循环(ReAct + LoopGuard) │ ├── config.py # 配置管理(完整 config.json 支持) │ ├── project_config.py # 项目级配置(.021-project.json) │ ├── llm.py # LLM 客户端(流式/非流式) │ ├── context.py # ⭐ 证据保留管理与自动压缩 │ ├── approval.py # 风险审批 / Ethics Review Board │ ├── observability.py # 审计日志、指标、Tracing │ ├── skill_metadata.py # Skill 元数据解析(frontmatter) │ ├── skill_loader.py # 多源 Skill 加载器(builtin/directory/remote) │ ├── prompt_manager.py # 双层提示工程(模板 + 角色) │ ├── agent_registry.py # Agent 注册表 │ ├── orchestrator.py # 多 Agent 编排(PI-Team/Handoff/Parallel) │ ├── task_dispatch.py # 并行子 Agent 调度 + 置信度聚合 │ ├── memory_pipeline.py # 两阶段记忆管道(提取 + 巩固) │ ├── memory_store.py # 记忆持久化(SQLite + 索引) │ ├── flow_control.py # 工具并发流控(FlowController) │ ├── resilience.py # 断路器 + 退避重试(CircuitBreaker) │ ├── permission_policy.py # 细粒度权限(白名单/黑名单) │ ├── tool_hooks.py # Hook 注册与执行 │ ├── hook_executor.py # 脚本 Hook 执行器(subprocess 隔离) │ ├── hook_config.py # Hook 配置加载 │ ├── config_watcher.py # 配置热加载监听 │ ├── trigger_matcher.py # 触发词索引(Progressive Disclosure) │ ├── mcp_client.py # MCP 客户端 │ ├── mcp_config.py # MCP 配置加载 │ ├── playback.py # 实验回放 │ ├── token_utils.py # tiktoken Token 统计与回退估算 │ ├── prompts/ # 提示模板目录 │ │ ├── system/ # 基础系统提示(base_scientific / safety_rules) │ │ └── agents/ # 角色专属提示 │ ├── cli/ │ │ ├── app.py # Rich CLI 界面 │ │ └── tui_app.py # Textual TUI 界面 │ └── tools/ │ ├── base.py # 工具抽象 │ ├── registry.py # 工具注册表 │ └── builtin/ # 24 个内置工具(shell、诊断、k8s、monitoring 等) ├── skill技能库/ # Skill markdown 文件 ├── .021-project.json # 项目级配置 ├── bin/ # npm wrapper 与 postinstall 脚本 ├── tests/ # 测试套件(180+ tests) │ ├── test_unit.py # 单元测试(无需网络) │ ├── test_agent.py # 集成测试(需要 021 模型) │ └── test_connection.py # 021 API 连接冒烟测试 ├── package.json # npm 包配置(未发布前需本地/Git 安装) ├── pyproject.toml # 项目配置与依赖 └── requirements.txt # Python 依赖 ``` ## 快速开始 ### 安装 #### 方式一:从当前源码目录通过 npm 安装(自动处理依赖) ```bash cd /path/to/021-agent-cli npm install -g . ``` 安装后可直接运行 `021`。npm 包会在安装时自动检测 Python 并通过 pip 装好依赖。 > 如果你看到 `npm install -g 021-agent` 返回 `E404`,说明该包尚未发布到公共 npm registry。 #### 方式二:通过 Git 安装 npm 包 ```bash npm install -g git+https://gitee.com/henryqi91/021-chat-cli.git ``` > **前置条件**:Node.js ≥ 16 + Python ≥ 3.10 > 若系统 Python 启用了 PEP 668 保护,脚本会自动改用 `--user` 重试;仍失败时按提示手动执行 `pip install .`。 #### 方式三:通过 pip 安装 ```bash # 从源码安装 pip install -e . # 或直接运行 python -m src.main ``` #### 通过 npm registry 安装(仅在发布后可用) ```bash npm install -g 021-agent ``` 仅当维护者已执行 `npm publish` 或发布到了你的私有 registry 时,此命令才可用。 #### 发布到 npm(维护者) ```bash # 1. 确保 package.json 的 version 与 pyproject.toml 一致 # 2. 打包预览(检查 files 白名单,不会带上 参考项目/、tests/) npm pack --dry-run # 3. 发布 npm publish # 内部/私有仓库:npm publish --registry ``` ### 配置 首次运行时会引导设置 API Key,也可通过环境变量配置。推荐使用 shell 友好的变量名: ```bash export ZERO21_API_KEY="your-api-key" export ZERO21_API_BASE="http://10.200.95.16:30300/v1" export ZERO21_MODEL="021-chat" ``` 代码也兼容旧环境变量 `021_API_KEY`、`021_API_BASE`、`021_BASE_URL`、`021_MODEL`。注意:以数字开头的变量名无法用普通 `export 021_API_KEY=...` 语法设置,只适合由进程环境或配置文件注入。 ### 运行 ```bash # TUI 模式(默认) 021 # CLI 模式 021 --cli # 单次命令 021 -c "检查服务器负载" # Plan 模式(只读,不执行写操作) 021 --mode plan # 列出可用工具 021 --list-tools # 列出可用模型 021 --list-models ``` ### 交互命令 在交互模式下,支持以下 `/` 命令。注意:TUI 与 Rich CLI 的命令集略有差异。 | 命令 | 说明 | |------|------| | `/help` | 显示帮助信息 | | `/quit` | 退出程序(Rich CLI);TUI 下会提示使用 `Ctrl+C` 退出 | | `/clear` | 清空会话历史 | | `/mode auto\|plan` | 切换运行模式 | | `/risk strict\|auto\|danger` | 切换风险策略 | | `/model [name]` | 切换或查看模型 | | `/history` | 查看会话历史(Rich CLI) | | `/stats` | 查看会话统计 | | `/tools` | 列出可用工具 | | `/experiments` | 实验记录管理 | | `/mcp` | MCP 服务器管理(TUI) | | `/metrics` | 查看指标摘要(TUI) | | `/think` | 启用思考模式(Rich CLI) | | `/no_think` | 禁用思考模式(Rich CLI) | | `/stream yes\|no` | 切换流式输出(Rich CLI) | | `/method`、`/hypothesis`、`/observe`、`/document`、`/verify`、`/report`、`/limitations` | 科学方法步骤快捷命令(Rich CLI) | --- ## ⭐ 证据保留 / 上下文压缩模块 这是 021 Agent 的核心创新之一,解决了 LLM 上下文窗口限制问题,支持**理论上无限长度的对话**。 ### 设计理念 ``` ┌─────────────────────────────────────────────────────────────┐ │ 消息列表 (messages) │ ├─────────────────────────────────────────────────────────────┤ │ system │ head(4条) │ middle(被压缩) │ tail(6条) │ │ prompt │ 早期上下文 │ LLM摘要替换区域 │ 最近工作记忆 │ └─────────────────────────────────────────────────────────────┘ ↓ 触发压缩 ┌─────────────────────────────────────────────────────────────┐ │ system │ head(4条) │ │ tail(6条) │ │ prompt │ │ 证据摘要+上一轮发现 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ ``` ### 核心机制 #### 1. Token 统计(tiktoken 优先,启发式回退) ```python def estimate_tokens(self, messages: list[dict]) -> int: return self._token_counter.count_tokens_in_messages(messages) ``` 默认使用 `tiktoken` 的 `cl100k_base` 编码统计消息 token;如果 `tiktoken` 不可用,才回退到 **字符数 ÷ 3** 的启发式估算。 #### 2. 溢出检测 ```python def should_compact(self, messages: list[dict]) -> bool: return self.estimate_tokens(messages) > self.soft_limit ``` **阈值配置**(针对 021 模型的 128K 上下文窗口): | 参数 | 值 | 说明 | |------|-----|------| | `soft_limit` | 80,000 | 触发压缩的阈值 | | `hard_limit` | 120,000 | 硬上限(用于计算利用率) | | `preserve_recent` | 6 | 保留最近 6 条消息 | | `preserve_head` | 4 | 保留前 4 条消息 | > 基于 DeepSeek-V2 MoE 架构,原始训练长度 32K,通过 YARN 外推(factor=4.0)扩展至 128K。 #### 3. LLM 驱动压缩 ```python async def llm_summarize_evidence(messages, llm_client): prompt = """Summarize the following conversation segment into a structured Markdown report. ## Goal What was the user trying to accomplish? ## Key Actions What tools were called and what were the key results? ## Findings & Decisions What was discovered? What decisions were made? ## Critical Context What must be remembered for the task to continue successfully? ## Next Steps What still needs to be done? """ ``` **结构化摘要格式**:Goal → Key Actions → Findings & Decisions → Critical Context → Next Steps #### 4. 累积摘要机制 多次压缩时,摘要会**层层累积保留**: ```python @property def summary(self) -> str: if not self._evidence_summaries: return "" parts = ["## Experimental Evidence Summary\n"] for i, s in enumerate(self._evidence_summaries, 1): parts.append(f"### Preservation Event {i}\n{s}") return "\n".join(parts) ``` **效果**:每次压缩时,将中间历史替换为 `` 证据摘要消息,并在后续压缩中保留上一轮关键发现,确保模型能继续使用被压缩掉的上下文。 #### 5. 双层回退策略 - **首选**:LLM 生成结构化 Markdown 摘要 - **回退**:启发式摘要(提取用户问题、工具调用次数、最后回复) ### 压缩后的消息结构示例 ```json [ {"role": "system", "content": "系统提示..."}, {"role": "user", "content": "早期问题1"}, {"role": "assistant", "content": "早期回答1"}, // ... 早期上下文 {"role": "user", "content": "\n[15 observations preserved into structured evidence — preservation #1]\n## Goal\n...\n"}, {"role": "assistant", "content": "Acknowledged — I'll continue with this preserved evidence."}, {"role": "user", "content": "最近问题1"}, // ... 最近6条消息 ] ``` ### 设计亮点 1. **三段保留策略**:system + 早期上下文 + 最近记忆,确保关键信息不丢失 2. **LLM 驱动压缩**:用模型生成结构化摘要,比简单截断更智能 3. **累积摘要**:多次压缩的摘要层层保留,形成长期记忆 4. **双层回退**:LLM 摘要失败时降级为启发式摘要 5. **UI 回调**:压缩时通知前端显示提示(如"⟳ Compacted 15 msgs → ~5000 tokens") --- ## 内置工具系统 ### 风险分级 | 级别 | 工具 | 说明 | |------|------|------| | **L0** (只读) | `shell`、`web_request`、`file_read`、诊断、查询、日志、DNS 等 | 无副作用,自动执行 | | **L1** (低风险写) | `k8s_exec`、`k8s_restart_pod`、`k8s_rollout_restart` 等 | `auto` 策略放行到审批层,首次使用通常需要确认 | | **L2** (高风险写) | `shell_write`、`k8s_scale` 等 | `auto` 默认阻止,需 `danger` 策略才会进入审批层 | ### 审批策略 风险策略和人工审批是两层控制: - **strict**:仅 L0 只读工具允许,L1/L2 在风险策略层阻止 - **auto**:L0 自动执行;L1 放行到审批层;L2 在风险策略层阻止(默认) - **danger**:L0/L1/L2 都放行到审批层(不推荐在生产环境使用) 审批层行为:L1 首次使用需要确认,后续相同参数可自动放行;L2 每次都需要确认。非交互模式或无法提供确认回调时,需要确认的工具会被拒绝。 ### 工具分组 当前共有 **24 个内置工具**: | 分组 | 工具 | |------|------| | 基础 | `shell`, `web_request`, `file_read`, `shell_write` | | 诊断 | `diagnose_disk`, `diagnose_memory`, `diagnose_cpu`, `diagnose_network`, `diagnose_processes`, `search_logs` | | 通用 | `wait_for` | | Kubernetes | `k8s_get_pods`, `k8s_describe_pod`, `k8s_get_logs`, `k8s_get_deployments`, `k8s_describe_node`, `k8s_exec`, `k8s_restart_pod`, `k8s_rollout_restart`, `k8s_scale` | | 监控 | `prometheus_query`, `prometheus_query_range` | | 网络 | `network_connectivity`, `network_dns_resolve` | ### 工具详情示例 ```python # shell - 执行 shell 命令 { "name": "shell", "risk_level": "L0", # 普通 shell 工具按只读观察使用 "parameters": { "command": "要执行的命令" } } # shell_write - 执行有写入/破坏风险的 shell 命令 { "name": "shell_write", "risk_level": "L2" } # file_read - 读取文件内容 { "name": "file_read", "risk_level": "L0", "parameters": { "path": "文件路径" } } # web_request - HTTP GET 请求 { "name": "web_request", "risk_level": "L0", "parameters": { "url": "请求地址" } } ``` --- ## 混合思考模式 Rich CLI 支持动态控制模型是否展示推理过程;默认 TUI 当前未暴露 `/think` / `/no_think` 命令。 ### 命令切换 ``` /think → 启用思考模式,模型会在最终答案前展示推理过程(Rich CLI) /no_think → 禁用思考模式,模型直接输出答案(Rich CLI) ``` ### 实现机制 ```python # LLM 请求中注入 thinking 参数 thinking = {"type": "enabled"} # /think thinking = {"type": "disabled"} # /no_think ``` ### 输出展示 启用思考模式时,CLI 会以灰色面板先展示推理过程,再展示最终答案: ``` ┌─────────────────────────────────────┐ │ Thinking Process │ │ 1. 分析用户请求... │ │ 2. 确定需要调用工具... │ └─────────────────────────────────────┘ 最终答案内容... ``` --- ## Agent 循环 采用 **ReAct 模式**(Reasoning + Acting): ``` 用户输入 → LLM 推理 → 工具调用 → 结果反馈 → LLM 推理 → ... → 最终回答 ``` ### 核心流程 1. **上下文检查**:是否需要压缩(`should_compact()`) 2. **LLM 调用**:流式/非流式获取响应,支持 thinking 参数控制 3. **工具执行**:根据风险级别决定是否需要审批 4. **循环检测**:LoopGuard 防止重复调用同一工具 5. **结果聚合**:收集 ObservationRecord(含 reasoning_content)用于统计 ### LoopGuard ```python class LoopGuard: max_repeats = 3 # 同一工具最多连续调用 3 次 ``` 防止模型陷入重复调用同一工具的死循环。 --- ## 配置说明 ### `~/.021-agent/config.json`(完整格式) ```json { "api_key": "sk-xxx", "base_url": "http://10.200.95.16:30300/v1", "model": "021-chat", "temperature": 0.1, "top_p": 0.9, "timeout": 120.0, "max_retries": 2, "model_registry": { "instruct": "021-236B-A21B-instruct", "reasoning": "021-236B-A21B-reasoning", "fast": "021-32B-A4B-reasoning" }, "default_model": "instruct", "agent": { "max_steps": 30, "risk_policy": "auto", "thinking_mode": "auto", "stream": false, "auto_approve_l1": false }, "skill_sources": ["builtin"], "user_skill_dir": "" } ``` 配置优先级:**环境变量 > config.json > dataclass 默认值** ### `.021-project.json`(项目级配置示例) 在项目根目录放置,可限定本项目启用的 Skill / Agent 与 Hook: ```json { "project_id": "my-project", "enabled_skills": ["query_pubchem", "blast_search"], "enabled_agents": ["researcher", "ops"], "hooks": { "PreToolUse": [ {"command": "python3 scripts/audit_log.py", "timeout": 5, "matcher": "*"} ], "PostToolUse": [ {"command": "python3 scripts/post_audit.py", "timeout": 5} ] } } ``` ### `~/.021-agent/hooks.json`(全局 Hook) ```json { "hooks": { "PreToolUse": [ {"command": "python3 scripts/validate.py", "timeout": 5, "matcher": "*"} ] } } ``` ### 默认配置 ```python LLMConfig( base_url="http://10.200.95.16:30300/v1", model="021-chat", temperature=0.1, top_p=0.9, ) AgentConfig( max_steps=30, context_compaction_threshold=80000, # 128K 上下文 context_soft_limit=120000, ) ``` ### 环境变量 | 变量 | 说明 | |------|------| | `021_API_KEY` | API 认证密钥 | | `ZERO21_API_KEY` | API 认证密钥(推荐,可直接 `export`) | | `ZERO21_API_BASE` | API 服务地址(推荐,可直接 `export`) | | `ZERO21_MODEL` | 模型名称(推荐,可直接 `export`) | | `021_API_BASE` | API 服务地址(推荐) | | `021_BASE_URL` | API 服务地址(兼容旧配置) | | `021_MODEL` | 模型名称 | --- ## 开发指南 ### 依赖 - Python ≥ 3.10 - httpx ≥ 0.27.0 - rich ≥ 13.0.0 - textual ≥ 0.50.0(默认 TUI 所需) - pytest / pytest-asyncio(开发测试依赖:`pip install -e .[dev]`) ### 运行测试 ```bash # 全量测试(180+ tests) python3 -m pytest tests/ -v # 单元测试(无需网络) pytest tests/test_unit.py -v # 集成测试(需要 021 模型与 API Key) ZERO21_RUN_INTEGRATION=1 pytest tests/test_agent.py -v # 连接冒烟测试(默认使用 http://10.200.95.16:30500/v1 和 021-chat-a100) ZERO21_RUN_CONNECTION=1 pytest tests/test_connection.py -v ``` ### 代码风格 - 类型注解:所有公开接口使用 type hints - 异步优先:IO 密集操作使用 async/await - 文档字符串:所有公开类和方法使用 Google 风格 docstring --- ## 架构与执行流程 ### 顶层模块 ``` ┌─────────────────────────────────────────────────────────────┐ │ Entry / Interface │ │ bin/021.js → python -m src.main │ │ ┌─────────────────────┐ ┌─────────────────────────────┐ │ │ │ Rich CLI (app.py) │ │ Textual TUI (tui_app.py) │ │ │ └─────────────────────┘ └─────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Agent Layer (agent.py) │ │ ReAct Loop · LoopGuard · ContextManager · FlowController │ │ MultiAgent (PI-Team/Handoff/Parallel) · AgentRegistry │ └─────────────────────────────────────────────────────────────┘ │ ┌──────────┬──────────┼──────────┬──────────┐ ▼ ▼ ▼ ▼ ▼ ┌────────────┐┌─────────┐┌─────────┐┌─────────┐┌──────────┐ │ LLM Client ││ToolHooks││ Skill ││ Memory ││Resilience│ │ + Prompt ││+Approval││ Loader ││ Manager ││CircuitBrk│ │ Manager ││+Scripts ││+Trigger ││(2-phase)││+FlowCtrl │ └────────────┘└─────────┘└─────────┘└─────────┘└──────────┘ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ OpenAI API subprocess Frontmatter SQLite Adaptive compatible isolation + Remote + Index Throttle ``` ### 单轮执行流程 ```text 用户输入 ↓ CLI/TUI 处理 slash 命令、会话历史与实验记录 ↓ Agent.run_experiment() ↓ 检查上下文长度;超过阈值则 EvidencePreservationManager.preserve() ↓ 按 mode 生成工具列表:plan 只暴露 L0;auto 暴露到 L2,再由风险策略拦截 ↓ LLMClient 调用 /chat/completions,支持工具调用与可选 thinking 参数 ↓ 如果模型请求工具:LoopGuard → risk_policy → ApprovalEngine → path sandbox → execute() ↓ ToolResult 写回消息列表,并记录 audit log / metrics / trace / experiment timeline ↓ 无更多工具调用时输出最终回答;否则继续下一轮观察 ``` ### 关键数据结构 | 结构 | 文件 | 作用 | |------|------|------| | `ObservationRecord` | `src/agent.py` | 单次实验观察记录,保存推理、结论、工具调用和 token 使用量 | | `ToolSpec` / `ToolResult` | `src/tools/base.py` | 工具定义与执行结果 | | `LLMResponse` / `StreamEvent` | `src/llm.py` | 非流式/流式模型响应封装 | | `EvidenceStats` | `src/context.py` | 上下文 token 使用率与压缩状态 | --- ## 许可证 内部项目,仅供团队使用。 --- ## 贡献 欢迎提交 Issue 和 Pull Request!