# FreeLoop **Repository Path**: zqhcoding/freeloop ## Basic Information - **Project Name**: FreeLoop - **Description**: A loop agent powered by Λ-Lang - **Primary Language**: 其他 - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2026-06-15 - **Last Updated**: 2026-09-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # FreeLoop — Lambda Agent ``` ░█▓░░ Λ-gent █▒░█▒ · █▒░█▒ ``` FreeLoop 是一个基于 [Λ-Lang](https://gitee.com/zqhcoding/alang) 的 **ReAct 风格**终端 AI 代理,支持多 LLM 提供商、12 个内置工具、16 种 TUI 组件和安全的沙箱执行。 ## 特性 - **ReAct 循环** — Thought → Action → Observation → finish,可配置最大步数 - **多 LLM 提供商** — OpenAI 兼容 API、Ollama(自动识别 `/v1/chat/completions`) - **12 个内置工具** — Bash、Read、Write、Edit、EditSearchReplace、Glob、Grep、WebFetch、WebSearch、Move、Copy、Finish - **安全优先** — SSRF 防护(拦截私有 IP)、预批准域名/文件/命令、破坏操作需用户确认 - **计划模式**(`/plan`)— 只读模式,仅允许 Read、Glob、Grep - **配置系统** — 优先级:环境变量 `FREELOOP_CONFIG_PATH` > 项目 `.freeloop.json` > 全局 `~/.config/freeloop/freeloop.json` - **丰富 TUI** — 16 种组件(Markdown、代码高亮、表格、滚动框、多行输入等),支持语法高亮和斜杠命令弹出菜单 - **Web UI** — 内置 HTTP 服务器,支持 SSE 实时流、CLI+浏览器同步交互、聊天专用组件渲染 - **20 个斜杠命令** — `/cache`、`/clear`、`/compact`、`/delete`、`/diff`、`/exit`、`/export`、`/help`、`/history`、`/init`、`/model`、`/new`、`/plan`、`/quit`、`/rename`、`/resume`、`/retry`、`/status`、`/strategy`、`/undo` - **多模型策略系统** — 11 种可插拔策略通过 `strategy_mgr.al` 路由:`basic`(单模型,默认)、`serial_failover`(有序故障转移)、`dual_backup`(主 + 显式备用)、`ensemble_vote`(并行投票)、`reviewer`(生成+审核+优化)、`task_router`(分类路由)、`speculative_race`(快速模型竞速)、`consensus_check`(Jaccard 一致性检测)、`refine_chain`(草稿→优化→润色流水线)、`cost_cascade`(低成本→高质量升级)、`moa`(模型混合,分层聚合)。运行时通过 `/strategy` 切换或在 `config.multiModel` 中配置 - **上下文窗口管理** — 近似 token 计数(字符数/4 启发式),当使用量超过 `compactThreshold`% 的 `contextWindow` 时自动压缩,支持模型特定上下文限制 - **OpenCode 插件模式**(`--opencode`)— 作为子进程与 OpenCode IDE 集成;统一 `/opencode-hook` V1 事件端点支持 50+ 钩子类型,Web UI 中结构化事件卡片渲染,自动允许权限,Shell 环境变量注入 - **会话持久化** — 可按时间排序的 `ses_` ID(base-36 时间戳 + 计数器),每个会话一个目录(`~/.config/freeloop/sessions//info.json` + 逐消息文件),支持保存/加载/列表/删除 - **客户端响应缓存** — LRU 文件缓存(200 条,1 小时 TTL),djb2 哈希键,减少重复 LLM 调用 - **服务端缓存追踪** — OpenAI `cached_tokens` 和 Anthropic `cache_*` 统计数据 - **Token 用量追踪** — 每次会话的提示/补全/请求数追踪,通过 `/status` 查看 - **对话压缩**(`/compact`)— 利用 LLM 将对话摘要为 3 条消息 - **AGENTS.md 生成**(`/init`)— 通过探索代码库自动创建项目指南 - **交互式模型切换**(`/model`)— 键盘导航弹出窗口切换提供商/模型 - **多引擎网页搜索** — 备用链:Bing CN → 360 → Sogou,纯 ALang 实现,支持域名过滤 - **HTML 转 Markdown** — 纯 ALang 实现的 WebFetch 结果转换器 - **思考指示器** — LLM 思考时的动画边框与 `Thinking... <已用秒数>s ↓ ` 行;按 ESC 可中断当前回合 - **键盘中断** — 按 ESC 可干净地取消正在进行的代理回合;Ctrl-C 清空输入草稿 - **SEARCH/REPLACE 差异编辑** — 支持多行上下文的精确编辑工具,输出变更摘要(`+X -Y 行`),适用于精确代码修改 - **Git 集成** — `/diff` 命令显示工作区变更;每次工具调用后自动显示 Git 变更作为观察结果 - **Markdown 渲染** — 助手输出支持语法高亮的代码块、标题样式和内联格式(加粗、斜体、行内代码) - **结构化日志** — 内置 `logging` 模块,支持层级 Logger、文件/终端输出、可配级别(DEBUG→CRITICAL),关键代理流程全覆盖 DEBUG/INFO/WARNING/ERROR - **调试日志** — 设置 `FREELOOP_DEBUG=true` 环境变量可记录完整 LLM 事务到 `~/.config/freeloop/transaction.json` - **首次运行自动配置** — 首次启动自动生成默认 `.freeloop.json` - **24-bit 真彩色** — 全 UI 使用 RGB 语义调色板 ## 使用示例 ![FreeLoop Demo](docs/freeloop_demo_v0.1.png) ## 快速开始 运行打包后的单文件二进制(通过 `al --pack` 一次性构建,见下文"构建与打包"): ```bat freeloop.exe ``` 或直接从源码运行(使用 alang 仓库构建出的 ALang 二进制): ```bash al repl.al # Linux/macOS .\output\bin\al.exe repl.al # Windows(在 alang 仓库根目录下) ``` 启动标志:`al repl.al --debug|--simu-oc|--opencode`(打包二进制上脚本参数需跟在 `--` 之后,如 `freeloop.exe -- --debug`)。等价的 `FREELOOP_DEBUG`/`FREELOOP_SIMU_OC`/`FREELOOP_OPENCODE` 环境变量仍然支持。 FreeLoop 不会切换工作目录:agent 始终以你运行命令时所在的目录作为工作区。 配置文件路径(按优先级从低到高,首个存在的生效): | 优先级 | 范围 | 路径 | |--------|------|------| | 1 | 环境 | `FREELOOP_CONFIG_PATH` 环境变量 | | 2 | 项目 | 当前目录下的 `.freeloop.json` | | 3 | 全局 | Linux/macOS: `~/.config/freeloop/freeloop.json` | | 3 | 全局 | Windows: `%USERPROFILE%/.config/freeloop/freeloop.json` 或 `%APPDATA%/freeloop/freeloop.json` | 完整配置示例: ```json { "defaultProvider": "ollama", "defaultModel": "qwen3:8b", "temperature": 0.0, "topP": 1.0, "maxTokens": 8192, "systemPrompt": "", "llmStreamDisplay": true, "llmStreamScope": "all", "requestTimeout": 600, "webTimeout": 20, "contextWindow": 32000, "compactThreshold": 75, "compactKeepMessages": 1, "autoApproveTools": true, "repetitionPenalty": 1.1, "frequencyPenalty": 0.0, "presencePenalty": 0.0, "topK": 0, "httpServer": { "enabled": true, "host": "127.0.0.1", "port": 8321 }, "logging": { "enabled": false, "file": "/tmp/freeloop.log", "level": "DEBUG" }, "multiModel": { "strategy": "basic" }, "providers": { "ollama": { "type": "ollama", "baseURL": "http://localhost:11434", "models": { "qwen3:8b": { "name": "qwen3:8b", "contextWindow": 32768 } } }, "xxx": { "type": "openai", "baseURL": "http://xxx.xxx/v1", "apiKey": "sk-xxx-xxx", "models": { "xxx": { "name": "xxx" } } } } } ``` `requestTimeout`(秒,默认 600)约束单次 LLM 调用及整个重试链的墙钟预算——最后 5 秒预留,避免启动无法完成的重试。`webTimeout`(秒,默认 20)约束 web 工具请求(如 `WebFetch`)。 `tools.timeoutSec`(秒,默认 60,最小 1;**0 = 不限制**)约束单次工具调用:工具协程超时后代理循环伪造超时观察并继续回合;设为 `0` 时完全跳过 shell 监督包装,长任务可运行至完成。`mouse.enabled`(默认 `false`)开启全会话鼠标追踪——追踪会捕获滚轮,默认关闭时终端原生滚轮回滚保持可用,而各弹窗打开期间仍会临时开启。`compactTimeout`(秒,默认 180)约束压缩摘要调用。 ### 配置参考 所有选项从配置文件顶层读取(除非特别说明)。 | 键 | 类型 | 默认值 | 描述 | |-----|------|--------|------| | `defaultProvider` | string | `"ollama"` | 当前提供商名(必须存在于 `providers`) | | `defaultModel` | string | `"qwen3:8b"` | 当前模型名(必须存在于该提供商的 `models`) | | `temperature` | number | `0.0` | 每次 LLM 调用的采样温度 | | `topP` | number | `1.0` | 请求体 `top_p` 字段 | | `maxTokens` | int | `8192` | 请求体 `max_tokens` 字段 | | `systemPrompt` | string | `""` | 覆盖内置系统提示;每回合重新读取,运行时修改立即生效 | | `llmStreamDisplay` | bool | `true` | 将结构化 LLM 回合逐字符流式输出,而非显示旋转动画 | | `llmStreamScope` | string | `"all"` | 哪些回合流式输出:`"all"`(每次模型调用,含工具 Thought)或 `"final"`(仅最终答案) | | `requestTimeout` | int (秒) | `600` | 单次 LLM HTTP 超时及整个重试链墙钟预算(最后 5 秒预留) | | `webTimeout` | int (秒) | `20` | web 工具(WebFetch)HTTP 超时 | | `maxSteps` | int | `50` | 单回合代理步数上限;**`0` = 不限制**;超出后回合返回安全上限观察 | | `tools.timeoutSec` | int (秒) | `60` | 单次工具调用预算(shell 监督 + 代理循环);**`0` = 不限制**;最小 1 | | `contextWindow` | int | 按模型/启发式(见下) | 覆盖压缩和"上下文已用 %"显示使用的上下文窗口 | | `compactThreshold` | int (%) | `75` | 当估算 token ≥ 窗口的该百分比时自动压缩 | | `compactKeepMessages` | int | `1` | 自动压缩时保留多少条尾部真实消息 | | `autoApproveTools` | bool | `true` | 设为 `false` 时,Write/Edit/Bash 在代理回合内触发交互式审批提示而非自动批准 | | `repetitionPenalty` | number | 未设置(ollama:自动 `1.1`) | 请求体 `repetition_penalty`;`type: "ollama"` 未设置时自动应用 1.1 以打破贪心解码重复循环 | | `frequencyPenalty` | number | 未设置 | 请求体 `frequency_penalty` | | `presencePenalty` | number | 未设置 | 请求体 `presence_penalty` | | `topK` | int | 未设置 | 请求体 `top_k` | | `httpServer` | dict | 见下 | HTTP 服务器开关/host/port | | `logging` | dict | 见下 | 结构化日志开关/文件/级别 | | `multiModel` | dict | 未设置(→ `basic`) | 多模型策略选择 + 各策略配置(见下) | | `providers` | dict | `{"ollama": ...}` | 提供商注册表;跨配置源深度合并 | > **注意:** `stop` 是旧示例遗留键,**代码从不读取**。`maxSteps` 会被读取:未设置/无效时默认 **50**;显式 **`0` 表示完全取消步数上限**(回合持续到模型完成或被中断)。`contextWindow`/`compactThreshold`/`compactKeepMessages` 控制回合内自动压缩。 ### HTTP 服务器 / Web UI FreeLoop 可以提供 Web UI,镜像所有 CLI 交互能力。CLI 和浏览器保持同步——代理事件同时流向两者。 在配置中添加 `httpServer`: ```json { "httpServer": { "enabled": true, "host": "127.0.0.1", "port": 8321 } } ``` **默认值:** - `enabled`: `true` — 代理模式下默认启动 HTTP 服务器(缺省视为开启,向后兼容) - `host`: `127.0.0.1` — 仅本机可访问(不对外暴露) - `port`: `8321` - `portFallback`: `true` — 配置端口被占用时,交互模式向上探测空闲端口(启动通知会打印实际端口);服务器模式保持固定端口。设为 `false` 则直接失败。 **使用方法:** 1. 启动 FreeLoop:`al repl.al`(或打包的 `freeloop.exe`)(服务器默认开启;设 `httpServer.enabled: false` 可禁用) 2. 在浏览器打开 `http://127.0.0.1:8321` 3. 通过网页输入框交互——功能与 CLI 一致 4. CLI 和浏览器实时同步显示同一对话 **架构:** - **SSE(Server-Sent Events)** 实时代理 → 浏览器流式输出 - **HTTP POST `/input`** 浏览器 → 代理输入 - **HTTP POST `/command`** 浏览器发送斜杠命令 - **语义事件渲染** — `events.al` 纯数据事件在 CLI 中经 `tuiapp/presenter.al` + `tuiapp/tui_sink.al` 渲染为 ANSI,浏览器中为 HTML/DOM - **同步状态** — `messages` 数组在 CLI 和 Web 间共享;`/state` 端点返回当前对话 JSON **Web UI 功能:** - **多会话侧栏** — 在多个并发会话间切换;按会话维护组件状态 - **流式输出** — `stream_delta` 事件累积,在 `done` 时将实时助手行重新渲染为 Markdown - **思考指示器** — LLM 思考时的动画指示器,含已用时间和 token 用量 - **中断按钮** — 顶栏按钮 → `POST /interrupt` 取消正在运行的回合 - **用量栏** — 顶栏 token/请求统计,每 5 秒从 `/api/usage` 轮询 - **下载按钮** — 导出会话 JSON(来自 `_rawEvents` 的原始事件,文件名 `freeloop_session__.json`) - **原始事件查看器** — 每条消息上的 `{…}` 按钮可在模态层显示原始事件 JSON(透明度 0,悬停显示) - **设置面板** — JSON 编辑器,由 `GET`/`PUT /api/config` 支持(密钥已掩码) - **主题切换** — 暗色/亮色主题 - **安全 Markdown 渲染器** — 先转义,支持代码/加粗/斜体/链接 **注意事项:** - HTTP 服务器默认绑定 `127.0.0.1` — 仅本机可访问 - 将 `host` 改为 `0.0.0.0` 会对外暴露(无内置认证,请谨慎使用) - 网页输入在 CLI 输入间隙处理——不会并发执行代理回合 - SSE 支持自动重连;刷新页面后从 `/state` 端点恢复历史对话 ### OpenCode 插件模式 FreeLoop 可以作为 [OpenCode](https://opencode.ai) IDE 的子进程运行,提供完整的 V1 钩子事件可观测性和拦截能力。 ```bash al repl.al --opencode # 或:freeloop.exe -- --opencode ``` 此模式执行以下操作: 1. 将插件(`webui/plugins/opencode/`)以 `file://` URL 形式注册到 OpenCode 配置中 2. 在后台启动 FreeLoop 的 HTTP 服务器(无头模式,无 TUI) 3. 启动 OpenCode,通过统一 `/opencode-hook` 端点连接到 FreeLoop 4. 在 OpenCode 退出时清理 FreeLoop 子进程 **架构:** - **统一钩子端点**(`POST /opencode-hook`)— 所有 50+ 种 V1 事件类型通过单一端点传输;钩子携带观察数据或可变输出用于拦截 - **拦截钩子** — FreeLoop 可修改 OpenCode 行为:自动允许 `permission.ask`、注入 `shell.env` 覆盖、修改 `chat.params`/`chat.headers`/`chat.message` 负载 - **流式文本缓冲区** — `message.part.delta` 事件被累积并在安全超时(3 秒)后刷新,实现实时渲染 - **结构化 Web UI** — OpenCode 事件以可折叠卡片形式呈现,具有类型特定样式(工具调用、命令、文件变更、权限、LSP 诊断、会话生命周期) - **丰富事件路由** — `runner.OnOpenCodeEvent()` 将 50+ 种钩子类型映射到 FreeLoop 事件系统:工具调用 → Action/Observation 事件、错误 → Error 事件、会话生命周期 → Thought 徽章 **配置:** 插件由 repl.al 的 `--opencode` 模式自动注册(它会改写 OpenCode 的 `opencode.json`,指向 `webui/plugins/opencode/`),无需手动设置。 **注意事项:** - `--opencode` 模式自动启动 HTTP 服务器;无需设置标准的 `httpServer` 配置 - OpenCode 插件(`webui/plugins/opencode/index.js`)是标准的 OpenCode V1 插件,通过 `file://` URL 加载 - `/opencode-hook` 请求有 5 秒超时;失败时优雅处理(输出保持不变) ### 结构化日志 FreeLoop 内置结构化日志模块(`stdlib/logging.al`),覆盖所有关键代理流程,按严重程度分级: | 级别 | 关键事件 | |------|----------| | `INFO` | AgentTurn 开始/结束、LLM API 调用、工具调度、会话保存/加载、命令调度、Web 输入、SSE 流、配置变更 | | `WARNING` | 工具超时、HTTP 404、LLM API 重试、配置保存失败、安全限制触发 | | `ERROR` | LLM 网络/空响应、未捕获异常 | | `DEBUG` | 步骤执行、SSE 推送、HTTP 轮询、Sink 广播、事件发送 | **配置:** ```json { "logging": { "enabled": true, "file": "/tmp/freeloop.log", "level": "DEBUG" } } ``` | 字段 | 类型 | 默认值 | 描述 | |------|------|--------|------| | `enabled` | bool | `false` | 开启/关闭日志输出。关闭时完全静默。 | | `file` | string | stdout | 日志文件路径。不设置时输出到终端。 | | `level` | string | `"DEBUG"` | 最低输出级别:`"DEBUG"`、`"INFO"`、`"WARNING"`、`"ERROR"`、`"CRITICAL"` | **覆盖范围:** 日志覆盖完整的请求生命周期——Web 输入 → 命令调度 → LLM API 调用 → 工具调度 → SSE 推送 → 会话持久化。模块级事件(sink 广播、SSE 流、轮询)记录为 DEBUG 级别。 ## HTTP API 内置服务器提供 REST + SSE 接口(默认 `127.0.0.1:8321`)。 | 方法 | 路径 | 用途 | |------|------|------| | `GET` | `/` `/index.html` `/style.css` `/app.js` | Web UI 资源 | | `GET` | `/events/live?seq=N` | **SSE 流** — 重放 `seq` 之后的缓冲事件,再推送实时事件(`data: \n\n`) | | `GET` | `/state` | 重放所有缓冲事件(按模式过滤)作为 `{"events": [...], "lastSeq": N}` | | `GET` | `/api/session` | `{"id": "ses_xxx"}` — 用于下载文件名的会话 ID | | `GET` | `/api/usage` | token 用量 + 缓存统计(Web UI 每 5 秒轮询) | | `GET` | `/api/config` | 完整配置 JSON,密钥已掩码(`apiKey` → 前 4 字符 + `***`) | | `PUT` | `/api/config` | 保存配置;还原掩码、校验、持久化;无效返回 `400` | | `POST` | `/approval` | 工具审批:body `{id?, decision: allow/deny/y/n/yes/no}` | | `POST` | `/interrupt` | 请求回合中断(对应 Web 中断按钮) | | `POST` | `/input` | 代理模式:`{"text": ...}` — 入队用户输入 | | `POST` | `/command` | 代理模式:`{"cmd": "/..."}` — 入队斜杠命令 | | `POST` | `/opencode-hook` | OpenCode 模式:统一 V1 钩子端点(见下) | 每个广播事件字典携带单调递增的 `seq`;缓冲上限 1000 条事件(`webui/http_server/sse_sink.al`)。 ## 提供商与模型结构 `providers` 是以提供商名为键的字典。每个条目: | 字段 | 类型 | 描述 | |-------|------|------| | `type` | string | `"openai"`(默认)、`"ollama"` 或 `"anthropic"` — 决定 URL 构建、工具参数格式和缓存控制处理 | | `baseURL` | string | API 基址。Ollama:追加 `/v1/chat/completions`(除非 `baseURL` 已以 `v1/` 结尾);其他:`/chat/completions` | | `apiKey` | string | 可选;存在时作为 `Authorization: Bearer` 发送;在 `/api/config` 中掩码 | | `models` | dict | 模型名 → 条目;字典**键**即模型名 | 每个模型条目(`providers..models.`): | 字段 | 类型 | 描述 | |-------|------|------| | `contextWindow` | int | 模型上下文窗口。缺省时按启发式:`128k`/`glm`→128000、`gpt-4o`→128000、`gpt-4`→8192、`claude`→200000、`32k`/`8b`/`70b`→32768,其余 32000 | ## 多模型策略 `multiModel.strategy` 选择当前策略(默认 `basic`;无效名称回退到 `basic`)。各策略配置从 `multiModel.` 读取。运行时通过 `/strategy ` 切换(仅内存,不持久化)。 | 策略 | 配置字段 | 行为 | |----------|---------------|----------| | `basic` | — | 通过 `defaultProvider`/`defaultModel` 单模型 | | `serial_failover` | — | 主模型出错时,依次尝试其他所有 provider×model;首个成功者胜 | | `dual_backup` | `backupProvider`, `backupModel` | 主模型 + 唯一一个显式备用模型 | | `ensemble_vote` | `models`(`{provider, model}` 列表) | 并行;文本 = 最长响应,结构化 = 按工具调用名多数投票 | | `reviewer` | `reviewerProvider`, `reviewerModel`, `maxRounds`(默认 `2`) | 生成 → 审核者(`APPROVED`/`FEEDBACK:`)→ 优化,最多 `maxRounds` 轮 | | `task_router` | `routes`(`{patterns, target}` 列表),`defaultTarget` | 对最后一条用户消息做大小写不敏感子串匹配 → 路由 | | `speculative_race` | `fastModel`, `primaryModel`, `timeoutSec`(默认 `3.0`) | 快速模型与主模型竞速;快速模型在时限内通过质量检查则胜 | | `consensus_check` | `models`(可选),`minModels`(默认 `3`),`agreementThreshold`(默认 `0.7`),`arbiterModel`(可选) | 并行多模型;Jaccard 词汇相似度门控,可选仲裁者综合 | | `refine_chain` | `chain`(`{provider, model, role}` 列表) | 分阶段 `draft`→`refine`→`polish`;仅最后一步接收 tools | | `cost_cascade` | `tiers`(`{provider, model, confidenceThreshold}` 列表) | 低价→高价;质量启发式(`0.0–1.0`)门控每一层 | | `moa` | `layers`(`{models, aggregate?}` 列表) | 模型混合;并行层 + 一个 `aggregate: true` 综合层 | ## 环境变量与 CLI | 变量 / 标志 | 用途 | |-----------------|---------| | `FREELOOP_CONFIG_PATH` | 配置文件路径(最高合并优先级) | | `FREELOOP_OPENCODE` | `"true"` → 无头 OpenCode 服务器模式(无 TUI) | | `FREELOOP_SIMU_OC` | `"true"` → 模拟 OpenCode 服务器模式(仅 Web UI) | | `FREELOOP_DEBUG` | `"true"`/`"1"` → 将每次 LLM 事务 JSON 行记录到 `~/.config/freeloop/transaction.json` | | `HOME` / `APPDATA` / `USERPROFILE` | 定位配置、会话(`sessions/`)、响应缓存(`cache/`)、历史(`history.json`) | | `FREELOOP_HOOK_URL` | OpenCode 插件钩子基址(默认 `http://127.0.0.1:8321/opencode-hook`) | | `--opencode` | `al repl.al --opencode` — 注册插件、启动无头服务器、启动 OpenCode、退出时清理 | | `--simu-oc` | `al repl.al --simu-oc` — 启动模拟 OC 服务器;通过 `tests/simulate_opencode.al` 驱动 | | `--debug` | `al repl.al --debug` — 等价于 `FREELOOP_DEBUG=true` | 标志由 repl.al 自行解析(`ApplyStartupFlags`,通过 `sys.opts`),在进程内设置对应的 `FREELOOP_*` 变量——标志优先于环境变量。打包二进制上脚本参数必须跟在 `--` 分隔符之后(如 `freeloop.exe -- --debug`);`--` 之后的所有参数都会转发给内嵌程序。 ## TUI 键盘快捷键 | 键 | 动作 | |-----|--------| | `Tab` | 从弹窗填充命令 / 插入 2 空格 | | `Shift+Tab` | 切换计划模式 | | `Enter` | 提交(单行)/ `Alt+Enter` 换行 | | `Ctrl-C` | 清空输入草稿 | | `ESC` | 中断运行中的回合 / 关闭弹窗 | | `Ctrl-D` | 强制退出(EOF) | | `Ctrl-L` | 重绘 | | `/`(首字符) | 打开命令弹窗,实时过滤 | ### 弹窗(`/model`、`/resume`、命令面板) - `Up` / `Down` 移动选中项,`Enter` 确认高亮项。 - `Esc` 关闭弹窗,单独按下即关闭,无需后续按键。 - `/resume` 内:`D` 确认删除(再按一次执行),`R` 行内重命名,可打印字符编辑名称,`Backspace` 删除一个码点。 ## 构建与打包 FreeLoop 是纯 ALang 实现——无需编译。两种方式获得可运行的 FreeLoop: **从源码运行**(需要 alang 仓库的二进制): ```bash cd agent/freeloop ../../output/bin/al repl.al # Linux/macOS ..\..\output\bin\al.exe repl.al # Windows ``` **打包单文件可执行**(内嵌解释器 + 字节码包 + 原生模块;目标机器无需安装 alang): ```bash cd agent/freeloop al --pack -o freeloop repl.al # -> freeloop.exe (Windows) / freeloop (Unix) ``` 注意事项: - `-o freeloop` 必须写在入口脚本**之前**(打包器据此确定输出名与应用标签) - 打包二进制首次启动会自解压到用户缓存目录(`%LOCALAPPDATA%` / `~/.cache`,按负载哈希分版本);之后启动复用缓存 - 打包二进制上启动标志需跟在 `--` 之后:`freeloop.exe -- --debug` ## 项目结构 ``` freeloop/ ├── repl.al # 主 REPL 循环(组合根;入口) ├── freeloop.exe # 打包单文件二进制(构建产物,不提交) ├── agent/ # 可嵌入核心包 — 不依赖任何前端 │ ├── core.al # ReAct 代理循环 │ ├── commands.al # 20 个斜杠命令处理器 │ ├── context.al # 上下文窗口管理与自动压缩 │ ├── llm.al # LLM API 客户端 │ ├── prompt.al # 系统提示词组装 │ ├── strategy_mgr.al # 多模型策略管理器(11 种策略) │ ├── strategies/ # 策略实现 │ │ ├── base.al # 策略基类 │ │ ├── basic.al # 单模型,无故障转移 │ │ ├── serial_failover.al # 有序模型故障转移 │ │ ├── dual_backup.al # 主模型 + 显式备用 │ │ ├── ensemble_vote.al # 并行多模型投票 │ │ ├── reviewer.al # 生成 + 审核 + 优化 │ │ ├── task_router.al # 分类路由到最佳模型 │ │ ├── speculative_race.al # 快速模型竞速主模型 │ │ ├── consensus_check.al # Jaccard 一致性检测 │ │ ├── refine_chain.al # 草稿 → 优化 → 润色流水线 │ │ ├── cost_cascade.al # 低成本 → 高质量升级 │ │ └── moa.al # 模型混合(分层聚合) │ ├── sink.al # 输出 sink 抽象 + Broadcast 注册 │ ├── events.al # 事件类型常量 + 工厂函数 │ ├── api.al # 公共门面:数据服务、SPI 注入、开放注册表 │ ├── hooks.al # 介入 hook 管线(veto/replace) │ ├── approval.al # 审批状态机(决策策略由宿主注入) │ ├── _config.al # 多源配置系统 │ ├── session.al # 会话持久化 │ ├── session_active.al # 活动会话跟踪 │ ├── response_cache.al # LRU 响应缓存 │ ├── mode.al # 计划模式单例 │ ├── cmd_list.al # 命令注册表(用于弹出菜单) │ ├── usage.al # Token 用量追踪 │ ├── debug.al # LLM 事务调试日志 │ ├── textutil.al # 文本工具(ANSI 清除等) │ ├── rawmode.al # 控制台原始模式恢复辅助 │ ├── interrupt.al # 键盘中断监控协程 │ ├── utf8.al # UTF-8 处理辅助 │ ├── prompts/ # 10 个模块化系统提示片段 │ └── tools/ # 内置工具实现(核心的一部分) │ ├── tools.al # bash/read/write/edit/editsearchreplace/glob/grep/ │ │ # webfetch/websearch/move/copy/finish + 注册表 │ └── _utils/ │ ├── preapproved.al # 预批准域名/文件/命令 │ ├── html_to_md.al # HTML 转 Markdown 转换器 │ ├── web_search.al # 多引擎网页搜索 │ ├── url_safety.al # SSRF 防护检查 │ └── cache.al # 通用 LRU 缓存 ├── tuiapp/ # 终端前端包 │ ├── tui/ # TUI 组件库(16 种组件) │ │ ├── tui.al # 终端状态管理器 │ │ ├── input.al # 多行输入文本框 │ │ ├── input_queue.al # ReadTextarea 输入队列 │ │ ├── layout_mgr.al # 布局管理器(滚动缓冲 + 覆盖层) │ │ ├── widget.al # 组件工厂 │ │ ├── layout.al # 布局引擎 │ │ ├── render.al # ANSI 渲染管线 │ │ ├── ansi.al # ANSI 转义码常量 │ │ ├── style.al # TUI 样式工具 │ │ ├── markdown.al # 基于 Token 的 Markdown 解析器 │ │ ├── code_tokenizer.al # 代码语法标记器 │ │ ├── model_select.al # 交互式模型选择弹出窗口 │ │ └── TUI_README.md # TUI 模块文档 │ ├── style.al # 24-bit 调色板与符号 │ ├── status.al # 旋转动画协程(由 repl 的 turn 事件监听驱动) │ ├── presenter.al # 语义事件 → widget 字典 │ ├── tui_sink.al # TUI 输出 sink(通过事件进行 ANSI 渲染) │ └── banner.al # 启动横幅生成器 ├── webui/ # Web 前端包 │ ├── http_server/ # Web UI HTTP 服务器模块 │ │ ├── handler.al # WebHandler 基类(策略模式) │ │ ├── server.al # HTTP 处理(SSE + 输入端点) │ │ ├── sse_sink.al # SSE 输出 sink(JSON 事件流) │ │ ├── web_base.al # 共享基础 HTML/CSS/JS 常量 │ │ └── web.al # 内嵌 HTML/CSS/JS 字符串常量 │ ├── agent_handler.al # 标准代理模式的 Web 处理器 │ └── plugins/opencode/ # OpenCode V1 插件集成 │ ├── index.js # V1 钩子定义(50+ 事件类型) │ ├── package.json # 插件清单 │ ├── opencode_handler.al # OpenCode 模式 WebHandler │ ├── runner.al # 事件路由 → FreeLoop 事件系统 │ └── web_opencode.al # 结构化事件卡片 Web UI ├── tests/ # 80+ 个测试套件 │ ├── strategies/ # 11 个策略专用测试 │ ├── e2e/ # 端到端 PTY/UI 测试 │ └── test_*.al # 模块/集成测试 └── docs/ # 文档与截图 ``` ## 斜杠命令 | 命令 | 描述 | |---------|------| | `/cache` | 显示缓存统计或清除响应缓存 | | `/clear` | 清空对话历史 | | `/compact` | 通过 LLM 压缩对话上下文 | | `/delete` | 删除已保存的会话 | | `/diff` | 显示工作区 Git 变更差异 | | `/exit` | 退出程序 | | `/export` | 将会话导出到文件 | | `/help` | 显示可用命令 | | `/history` | 显示对话历史 | | `/init` | 创建/刷新 AGENTS.md | | `/model` | 显示或切换当前模型 | | `/new` | 开始新对话 | | `/plan` | 切换只读计划模式 | | `/rename` | 重命名当前会话 | | `/resume` | 恢复之前的会话 | | `/retry` | 重试上一个用户查询 | | `/status` | 显示 Token 用量和状态 | | `/strategy` | 显示或切换多模型策略 | | `/undo` | 撤销上一个代理回合 | ## 工具 | 工具 | 描述 | 安全性 | |------|------|--------| | Bash | 执行 Shell 命令 | 需 y/N 确认(除非预批准) | | Read | 读取文件内容 | 无限制 | | Write | 创建/覆盖文件 | 需 y/N 确认(除非预批准) | | Edit | 文件中精确替换字符串 | 需 y/N 确认(除非预批准) | | EditSearchReplace | 多行上下文感知的 SEARCH/REPLACE,输出变更摘要 | 需 y/N 确认(除非预批准) | | Glob | 按名称模式搜索文件 | 无限制 | | Grep | 按正则表达式搜索内容 | 无限制 | | WebFetch | HTTP GET 并转换为 Markdown | SSRF 防护,LRU 缓存 | | WebSearch | 多引擎网页搜索 | 域名过滤 | | Move | 移动/重命名文件 | 需 y/N 确认(除非预批准) | | Copy | 复制文件/目录 | 需 y/N 确认(除非预批准) | | Finish | 以最终答案结束代理回合 | 无限制 | ## 许可证 Apache 2.0