diff --git a/README.md b/README.md index 41317fa7..c8d3943a 100644 --- a/README.md +++ b/README.md @@ -1,129 +1,268 @@ # NeoCode -> 基于 Go + Bubble Tea 的本地 Coding Agent +NeoCode 是一个基于 Go 和 Bubble Tea 的本地 Coding Agent MVP。 -NeoCode 是一个在终端中运行的 AI 编码助手,采用 ReAct(Reason-Act-Observe)循环模式,能够自主推理、调用工具并完成任务。 +它当前聚焦一条最重要的主链路: -## 核心特性 +`用户输入 -> Agent 推理 -> 调用工具 -> 获取结果 -> 继续推理 -> UI 展示` -- **流式输出** — 实时展示模型思考过程 -- **工具系统** — 文件操作、代码执行、搜索等内置工具 -- **多 Provider 支持** — OpenAI、Gemini 等主流模型,易于扩展 -- **终端原生体验** — 基于 Bubble Tea 的现代化 TUI +如果你是第一次打开这个仓库,可以先把它理解成一个运行在终端里的本地 AI 编码助手。它会在 TUI 中接收你的问题,调用模型进行推理,在需要时使用本地工具,再把过程和结果实时展示出来。 -## 快速开始 +## 这份 README 适合谁 -### 环境要求 +- 想先把项目跑起来的新同学 +- 想快速看懂仓库分层的贡献者 +- 想知道配置文件、Provider、Tools 应该放在哪一层的协作者 -- Go 1.21+ -- API Key(OpenAI 或 Google Gemini) +如果你想直接看详细设计,可以跳到本文末尾的“文档索引”。 -### 安装与运行 +## 当前能力 -```bash -# 克隆仓库 -git clone https://github.com/yourusername/neocode.git -cd neocode +当前仓库已经围绕 MVP 主链路组织,重点在于把边界理顺,而不是一次性做很多功能。 -# 设置 API Key -export OPENAI_API_KEY=your_key_here +- 终端 TUI 交互界面 +- Runtime 驱动的 ReAct 主循环 +- Provider 抽象与内建 Provider 配置 +- Tool Registry 和统一工具协议 +- 本地 Session 持久化 +- 流式事件回传到 TUI -# 运行 -go run ./cmd/neocode -``` +当前内建工具: + +- `filesystem_read_file` +- `filesystem_write_file` +- `filesystem_grep` +- `filesystem_glob` +- `filesystem_edit` +- `bash` +- `webfetch` + +当前内建 Provider 配置: + +| Provider | 默认模型 | API Key 环境变量 | 说明 | +| --- | --- | --- | --- | +| `openai` | `gpt-4.1` | `OPENAI_API_KEY` | 默认配置 | +| `gemini` | `gemini-2.5-flash` | `GEMINI_API_KEY` | 走 OpenAI-compatible 接口 | +| `openll` | `gpt-4.1` | `AI_API_KEY` | 自定义 OpenAI-compatible 入口 | + +## 快速开始 -### 基本使用 +### 1. 准备环境 -在 TUI 中输入自然语言指令,例如: +- Go `1.25.0` +- 一个可用的模型 API Key +- 可正常联网的终端环境 +### 2. 设置 API Key + +NeoCode 不会把明文 API Key 写入 `config.yaml`。你可以直接设置系统环境变量,也可以放到 `.env` 文件中。 + +常见方式: + +```powershell +$env:OPENAI_API_KEY="your-api-key" ``` -帮我看看当前项目的目录结构 -创建一个 HTTP 服务器,监听 8080 端口 -分析 runtime.go 的主要逻辑 + +或在项目根目录 / `~/.neocode/.env` 中写入: + +```env +OPENAI_API_KEY=your-api-key ``` -使用 slash 命令快速切换配置: +可选环境变量名取决于你使用的 Provider: -- `/provider` — 切换模型提供商 -- `/model` — 切换模型 +- `OPENAI_API_KEY` +- `GEMINI_API_KEY` +- `AI_API_KEY` -## 架构概览 +你也可以在进入 TUI 后通过 `/apikey` 打开输入框,自定义当前运行时优先读取的 API Key 环境变量名。留空确认会恢复为当前 Provider 的默认环境变量名。 +### 3. 启动应用 + +```bash +go run ./cmd/neocode ``` -┌─────────────────────────────────────────┐ -│ TUI (Bubble Tea) │ -└────────────────┬────────────────────────┘ - │ Events -┌────────────────▼────────────────────────┐ -│ Runtime (ReAct Loop) │ -└────────┬───────────────────┬────────────┘ - │ │ - ┌────▼─────┐ ┌────▼──────┐ - │ Provider │ │ Tools │ - │ (LLM) │ │ Registry │ - └──────────┘ └───────────┘ + +首次启动时,程序会自动创建默认配置文件: + +`~/.neocode/config.yaml` + +### 4. 在界面里开始使用 + +进入 TUI 后,你可以直接输入需求,例如: + +- “帮我阅读当前仓库结构并总结” +- “查看 `internal/runtime` 的主循环逻辑” +- “搜索项目里和 provider 相关的实现” + +常用 Slash Command: + +- `/provider`:切换当前 Provider +- `/model`:切换当前模型 +- `/apikey`:设置全局 API Key 环境变量名覆盖 + +## 新手先理解这 4 层 + +如果你只想快速看懂项目,先抓住下面这条链路: + +`TUI -> Runtime -> Provider / Tools` + +### TUI + +`internal/tui` + +- 负责界面展示、输入处理、Slash Command 和状态切换 +- 只消费 Runtime 事件,不直接执行工具,不直接调用厂商 API + +### Runtime + +`internal/runtime` + +- 是整个 Agent 的编排中心 +- 负责维护会话、组织上下文、驱动模型调用、执行工具回灌、控制停止条件 + +### Provider + +`internal/provider` + +- 负责抹平不同模型厂商之间的协议差异 +- 对 Runtime 暴露统一的 `ChatRequest / ChatResponse / ToolCall / StreamEvent` + +### Tools + +`internal/tools` + +- 负责所有可被模型调用的本地能力 +- 包括 schema 定义、参数校验、执行、错误包装和结果收敛 + +一句话记忆: + +不要跨层直连。UI 不碰工具执行,Runtime 不写厂商协议细节,工具能力统一进入 `internal/tools`。 + +## 配置说明 + +默认配置文件路径: + +`~/.neocode/config.yaml` + +当前落盘配置是“最小状态配置”,只保存当前选择和通用运行参数,不保存完整 Provider 元数据。 + +示例: + +```yaml +selected_provider: openai +current_model: gpt-4.1 +api_key_env_override: CUSTOM_OPENAI_KEY +workdir: /absolute/path/to/workspace +shell: powershell +max_loops: 8 +tool_timeout_sec: 20 +tools: + webfetch: + max_response_bytes: 262144 + supported_content_types: + - text/html + - application/xhtml+xml + - text/plain + - application/json + - application/xml + - text/xml ``` -核心模块职责: +几个关键点: + +- `selected_provider`:当前选中的 Provider +- `current_model`:当前 Provider 下使用的模型 +- `workdir`:工具默认工作目录;启动后会被规范化为绝对路径 +- `shell`:Windows 默认是 `powershell`,其他系统默认是 `bash` +- `max_loops`:Runtime 最大推理轮数 +- `tool_timeout_sec`:工具超时时间 + +说明: + +- 示例里的 `workdir` 使用绝对路径是为了贴近真实落盘结果 +- 如果你首次启动时还没有手动配置,程序会根据当前工作目录生成默认值 + +Provider 的 `base_url`、默认模型列表和 `api_key_env` 由代码内建定义提供,不需要你在 YAML 中手动维护;如果你想全局覆盖当前运行时读取的环境变量名,可以设置顶层的 `api_key_env_override`,留空则回退到当前 Provider 的默认值。 + +当前代码内建的 Provider 包括 `openai`、`gemini` 和 `openll`。 + +## 工具与安全边界 + +NeoCode 当前默认遵守这些约束: -- **`internal/config`** — 配置管理、环境变量、YAML 加载 -- **`internal/provider`** — LLM 提供商抽象,抹平厂商差异 -- **`internal/runtime`** — ReAct 主循环、事件流、会话管理 -- **`internal/tools`** — 工具注册表与具体工具实现 -- **`internal/tui`** — 终端 UI、交互体验、事件桥接 -- **`internal/app`** — 应用装配与依赖注入 +- `filesystem` 工具限制在工作目录内 +- `bash` 工具有超时控制,避免长时间阻塞 +- `webfetch` 限制响应大小和内容类型 +- API Key 通过环境变量读取,不写入聊天记录和配置文件 -## 目录结构 +如果你要新增一个可被模型调用的能力,优先放进 `internal/tools`,不要直接写到 `runtime` 或 `tui` 里。 + +## 仓库结构 ```text . -├── cmd/neocode # CLI 入口 -├── docs # 架构与设计文档 -│ ├── guides # 使用指南 -│ └── *.md # 设计文档 -├── internal -│ ├── app # 应用装配 -│ ├── config # 配置管理 -│ ├── provider # Provider 抽象与实现 -│ ├── runtime # ReAct 循环与事件流 -│ ├── tools # 工具系统 -│ └── tui # 终端 UI -└── README.md +├── cmd/neocode # CLI 入口 +├── internal/app # 应用装配与依赖注入 +├── internal/config # 配置加载、保存、校验、并发安全访问 +├── internal/provider # Provider 抽象、驱动注册、厂商适配 +├── internal/runtime # ReAct 主循环、事件、Session 管理 +├── internal/tools # 工具协议、注册表、具体工具实现 +├── internal/tui # Bubble Tea 界面与交互状态机 +└── docs # 架构与细节设计文档 ``` -## 文档 +## 开发命令 + +启动应用: -- **[配置指南](docs/guides/configuration.md)** — Provider 策略、配置文件、环境变量 -- **[扩展 Provider](docs/guides/adding-providers.md)** — 如何添加新的模型提供商 -- **[架构设计](docs/neocode-coding-agent-mvp-architecture.md)** — 整体架构与设计理念 -- **[事件流](docs/runtime-provider-event-flow.md)** — Runtime 与 Provider 的事件交互 +```bash +go run ./cmd/neocode +``` -## 开发 +编译全部包: ```bash -# 格式化代码 -gofmt -w ./cmd ./internal +go build ./... +``` + +运行测试: -# 运行测试 +```bash go test ./... +``` -# 编译 -go build ./... +格式化代码: + +```bash +gofmt -w ./cmd ./internal ``` -## 当前状态 +## 贡献时建议先看 + +这个仓库最重要的协作原则是: + +- 优先保证主链路可运行 +- 优先保持模块边界清晰 +- 新能力默认沿 `TUI -> Runtime -> Provider / Tool Manager` 接入 +- 修改 `config`、`provider`、`runtime`、`tools` 时要同步评估测试 -NeoCode 正处于 MVP 阶段,核心闭环已可用: +在开始改代码前,建议先阅读仓库根目录的 `AGENTS.md`。 -✅ 用户输入 → Agent 推理 → 工具调用 → 结果返回 → UI 展示 +## 文档索引 -正在持续迭代中,重点关注: +- `docs/guides/configuration.md` +- `docs/guides/adding-providers.md` +- `docs/neocode-coding-agent-mvp-architecture.md` +- `docs/runtime-provider-event-flow.md` +- `docs/config-management-detail-design.md` +- `docs/provider-schema-strategy.md` +- `docs/tools-and-tui-integration.md` +- `docs/session-persistence-design.md` -- 📚 文档完善 -- 🧪 测试覆盖率 -- 🛠️ 工具能力扩展 -- 🔧 稳定性与性能 +## 一句话总结 +NeoCode 当前不是一个“功能很多”的 Agent,而是一个把主链路、分层和可验证性打磨清楚的 Coding Agent MVP。对新同学来说,最好的阅读顺序是:先跑起来,再看 `README` 的分层说明,最后按需进入 `docs/` 深入细节。 ## License MIT diff --git a/docs/config-management-detail-design.md b/docs/config-management-detail-design.md index 8975eb90..bb6859fe 100644 --- a/docs/config-management-detail-design.md +++ b/docs/config-management-detail-design.md @@ -3,32 +3,33 @@ `config` 模块主要负责四类事情: - 加载和保存 YAML 配置文件 - 从环境变量解析真实密钥 -- 管理 NeoCode 托管目录中的配置与 `.env` +- 管理 NeoCode 托管目录中的 `.env` - 向运行中的系统提供并发安全的配置读写能力 ## 核心类型 -- `Config`:顶层应用配置,运行时包含 Provider 列表、当前选中 Provider、当前模型、工作目录、Shell 和循环限制等信息 +- `Config`:顶层应用配置,运行时包含 Provider 列表、当前选中的 Provider、当前模型、全局 `api_key_env_override`、工作目录、Shell 和循环限制等信息 - `ProviderConfig`:单个 Provider 的内建定义,包括 Base URL、默认模型、实例级模型列表和 API Key 环境变量名 - `Manager`:使用 `sync.RWMutex` 保护的配置访问器与修改器 - `Loader`:对 YAML 文件和托管 `.env` 文件的文件系统封装 ## 环境变量策略 -- YAML 不保存 `api_key_env`,只保存 `selected_provider`、`current_model` 和通用运行配置。 -- `Loader.LoadEnvironment` 会尝试加载当前工作目录下的 `.env` 和 NeoCode 托管目录中的 `.env`。 -- `ProviderConfig.ResolveAPIKey` 在真正发起请求前通过 `os.Getenv` 读取密钥。 +- YAML 不保存 Provider 元数据,但允许保存顶层 `api_key_env_override`,与 `selected_provider`、`current_model` 和通用运行配置一起持久化 +- `Loader.LoadEnvironment` 会尝试加载当前工作目录下的 `.env` 和 NeoCode 托管目录中的 `.env` +- `ProviderConfig.ResolveAPIKey` 在真正发起请求前通过 `os.Getenv` 读取密钥 ## 运行时更新 -- TUI 只能通过 `ConfigManager.Update` 修改配置。 -- TUI 只负责切换当前 Provider 和当前模型,不直接修改 Provider 元数据。 -- `base_url`、`api_key_env`、`driver` 和 `models` 由代码内建定义提供,不从 YAML 读写。 -- 修改模型时,只更新 `current_model`;当前 Provider 的 `model` 仍表示默认模型,`models` 负责描述该实例可选模型列表。 +- TUI 只通过 `ConfigManager.Update` 修改配置 +- TUI 可以切换当前 Provider、当前模型,以及设置全局 `api_key_env_override`,但不直接修改 Provider 元数据 +- `base_url`、`api_key_env`、`driver` 和 `models` 由代码内建定义提供,不从 YAML 读写 +- 修改模型时,只更新 `current_model`;当前 Provider 的 `model` 仍表示默认模型,`models` 负责描述该实例可选模型列表 ## 默认值治理 -- 默认 Provider 名称、URL、默认模型、模型列表和环境变量名统一由内建 Provider 定义提供。 -- `Loader` 在加载旧配置时会丢弃 `providers` / `provider_overrides`,重新回到“YAML 只保存选择状态”的最小结构。 -- Provider 的可选模型目录属于实例配置,进入运行时 `Config` 后再提供给 TUI 和 runtime,避免 TUI 或 driver 自己维护一套零散常量。 +- 默认 Provider 名称、URL、默认模型、模型列表和环境变量名统一由内建 Provider 定义提供 +- 当前内建 Provider 包括 `openai`、`gemini` 和 `openll` +- `Loader` 在加载旧配置时会丢弃 `providers` / `provider_overrides`,重新回到“YAML 只保存选择状态和顶层 override”的最小结构 +- Provider 的可选模型目录属于实例配置,进入运行时 `Config` 后再提供给 TUI 和 runtime,避免 TUI 或 driver 自己维护一套零散常量 ## 安全约束 -- 读操作统一走 `Get`,并返回拷贝后的配置快照。 -- 写操作统一走 `Update`,修改前后都要做校验。 -- 真实密钥不能出现在日志、状态栏、聊天流或错误提示中。 +- 读操作统一走 `Get`,并返回拷贝后的配置快照 +- 写操作统一走 `Update`,修改前后都要做校验 +- 真实密钥不能出现在日志、状态栏、聊天流或错误提示中