Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 104 additions & 38 deletions README.md
Comment thread
phantom5099 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,63 +1,129 @@
# NeoCode

NeoCode 是一个基于 Go Bubble Tea 的本地编码 Agent。它在终端中运行 ReAct 闭环,能够对话、调用工具、持久化会话,并以流式方式展示模型输出。
> 基于 Go + Bubble Tea 的本地 Coding Agent

## 当前 Provider 策略
NeoCode 是一个在终端中运行的 AI 编码助手,采用 ReAct(Reason-Act-Observe)循环模式,能够自主推理、调用工具并完成任务。

- 内建 provider 定义随代码版本发布。
- `config.yaml` 不再持久化完整 `providers` 列表。
- `config.yaml` 只保存当前选择状态和通用运行配置。
- 运行时的 `providers` 完全来自代码内建定义。
- API Key 只从环境变量读取,不写入 YAML。
- 当前内建 provider 包括 `openai` 和 `gemini`。
- `gemini` 复用 OpenAI-compatible driver,请求地址指向 Gemini 的兼容接口。
- provider 实例自己定义 `base_url`、默认模型、可选模型列表和 `api_key_env`。
- `base_url` 不在 TUI 中展示给用户。
- driver 只负责协议构造与响应解析,不决定 `models`、`base_url` 或 `api_key_env`。
## 核心特性

这意味着:
- **流式输出** — 实时展示模型思考过程
- **工具系统** — 文件操作、代码执行、搜索等内置工具
- **多 Provider 支持** — OpenAI、Gemini 等主流模型,易于扩展
- **终端原生体验** — 基于 Bubble Tea 的现代化 TUI

- 新用户启动后会自动拿到当前版本最新的内建 provider。
- 未来代码新增 provider 时,新用户不需要修改 YAML。
- 老配置文件中的 `providers` / `provider_overrides` 会在加载时被清理为新的最小状态格式。
## 快速开始

## 配置文件
### 环境要求

默认路径:
[`~/.neocode/config.yaml`](~/.neocode/config.yaml)
- Go 1.21+
- API Key(OpenAI 或 Google Gemini)

当前落盘结构示例:
### 安装与运行

```yaml
selected_provider: openai
current_model: gpt-5.4
workdir: .
shell: powershell
max_loops: 8
tool_timeout_sec: 20
```bash
# 克隆仓库
git clone https://github.com/yourusername/neocode.git
cd neocode

# 设置 API Key
export OPENAI_API_KEY=your_key_here

# 运行
go run ./cmd/neocode
```

其中:
### 基本使用

在 TUI 中输入自然语言指令,例如:

```
帮我看看当前项目的目录结构
创建一个 HTTP 服务器,监听 8080 端口
分析 runtime.go 的主要逻辑
```

- `selected_provider` 和 `current_model` 是用户当前选择。
- provider 的 `base_url`、`models`、`api_key_env` 和 `driver` 都由开发者在代码中预设。
- `openai` 默认读取 `OPENAI_API_KEY`,`gemini` 默认读取 `GEMINI_API_KEY`。
- 完整 provider 列表不落盘,用户不需要在 YAML 中维护供应商元数据。
使用 slash 命令快速切换配置:

## Slash Commands
- `/provider` — 切换模型提供商
- `/model` — 切换模型

- `/provider`:打开 provider 选择器。
- `/model`:打开当前 provider 的模型选择器。
## 架构概览

## 运行
```
┌─────────────────────────────────────────┐
│ TUI (Bubble Tea) │
└────────────────┬────────────────────────┘
│ Events
┌────────────────▼────────────────────────┐
│ Runtime (ReAct Loop) │
└────────┬───────────────────┬────────────┘
│ │
┌────▼─────┐ ┌────▼──────┐
│ Provider │ │ Tools │
│ (LLM) │ │ Registry │
└──────────┘ └───────────┘
```

```bash
go run ./cmd/neocode
核心模块职责:

- **`internal/config`** — 配置管理、环境变量、YAML 加载
- **`internal/provider`** — LLM 提供商抽象,抹平厂商差异
- **`internal/runtime`** — ReAct 主循环、事件流、会话管理
- **`internal/tools`** — 工具注册表与具体工具实现
- **`internal/tui`** — 终端 UI、交互体验、事件桥接
- **`internal/app`** — 应用装配与依赖注入

## 目录结构

```text
.
├── cmd/neocode # CLI 入口
├── docs # 架构与设计文档
│ ├── guides # 使用指南
│ └── *.md # 设计文档
├── internal
│ ├── app # 应用装配
│ ├── config # 配置管理
│ ├── provider # Provider 抽象与实现
│ ├── runtime # ReAct 循环与事件流
│ ├── tools # 工具系统
│ └── tui # 终端 UI
└── README.md
```

## 文档

- **[配置指南](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
# 格式化代码
gofmt -w ./cmd ./internal

# 运行测试
go test ./...

# 编译
go build ./...
```

## 当前状态

NeoCode 正处于 MVP 阶段,核心闭环已可用:

✅ 用户输入 → Agent 推理 → 工具调用 → 结果返回 → UI 展示

正在持续迭代中,重点关注:

- 📚 文档完善
- 🧪 测试覆盖率
- 🛠️ 工具能力扩展
- 🔧 稳定性与性能

## License

MIT
185 changes: 185 additions & 0 deletions docs/guides/adding-providers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# 扩展 Provider

本文档说明如何为 NeoCode 添加新的 Provider。

## 架构概览

NeoCode 的 provider 分为两层:

- **配置层**(`ProviderConfig`):提供 `base_url`、模型列表、API Key 环境变量等元数据
- **驱动层**(`DriverDefinition`):负责实际的 API 协议差异

一个驱动可被多个 provider 复用(如 `gemini` 和 `openll` 都复用 `openai` 驱动)。

## 方式一:复用已有驱动(推荐)

适用于 OpenAI 兼容接口。只需添加配置文件,无需编写新的驱动代码。

### 示例:添加 DeepSeek

**1. 创建 `internal/provider/deepseek/deepseek.go`:**

```go
package deepseek

import (
"neo-code/internal/config"
)

const (
Name = "deepseek"
DriverName = "openai" // 复用 openai 驱动
DefaultBaseURL = "https://api.deepseek.com/v1"
DefaultModel = "deepseek-chat"
DefaultAPIKeyEnv = "DEEPSEEK_API_KEY"
)

var builtinModels = []string{
DefaultModel,
"deepseek-coder",
}

// BuiltinConfig 返回该 provider 的内建配置。
func BuiltinConfig() config.ProviderConfig {
return config.ProviderConfig{
Name: Name,
Driver: DriverName,
BaseURL: DefaultBaseURL,
Model: DefaultModel,
Models: append([]string(nil), builtinModels...),
APIKeyEnv: DefaultAPIKeyEnv,
}
}
```

**2. 在 `internal/provider/builtin/builtin.go` 中添加该 provider:**

```go
import "neo-code/internal/provider/deepseek"

func DefaultConfig() *config.Config {
cfg := config.Default()
defaultProvider := openai.BuiltinConfig()
cfg.Providers = []config.ProviderConfig{
defaultProvider,
gemini.BuiltinConfig(),
openll.BuiltinConfig(),
deepseek.BuiltinConfig(), // 新增
}
// ...
}
```

## 方式二:实现新驱动

适用于协议不兼容的厂商(如 Anthropic、Google 原生 API)。

### 示例:添加 Anthropic

**1. 在 `internal/provider/anthropic/anthropic.go` 中实现核心类型:**

```go
package anthropic

import (
"context"
"neo-code/internal/config"
domain "neo-code/internal/provider"
)

const (
Name = "anthropic"
DriverName = "anthropic"
DefaultBaseURL = "https://api.anthropic.com/v1"
DefaultModel = "claude-sonnet-4-20250514"
DefaultAPIKeyEnv = "ANTHROPIC_API_KEY"
)

type Provider struct {
cfg config.ResolvedProviderConfig
client *http.Client
}

// New 构造函数,接收已解析的配置。
func New(cfg config.ResolvedProviderConfig) (*Provider, error) {
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("anthropic provider: %w", err)
}
// ...
return &Provider{cfg: cfg, client: &http.Client{}}, nil
}

// Chat 实现 domain.Provider 接口。
// 必须支持流式输出(通过 events channel)和 tool calls。
func (p *Provider) Chat(ctx context.Context, req domain.ChatRequest, events chan<- domain.StreamEvent) (domain.ChatResponse, error) {
// 1. 将 domain.ChatRequest 转换为厂商特定的请求格式
// 2. 调用厂商 API(流式 SSE)
// 3. 解析响应,推送 StreamEventTextDelta / StreamEventToolCallStart
// 4. 返回 domain.ChatResponse
}

// Driver 返回驱动定义,供 Registry 注册使用。
func Driver() domain.DriverDefinition {
return domain.DriverDefinition{
Name: Name,
Build: func(ctx context.Context, cfg config.ResolvedProviderConfig) (domain.Provider, error) {
return New(cfg)
},
}
}

// BuiltinConfig 返回内建配置。
func BuiltinConfig() config.ProviderConfig {
return config.ProviderConfig{
Name: Name,
Driver: DriverName,
BaseURL: DefaultBaseURL,
Model: DefaultModel,
Models: []string{DefaultModel, "claude-opus-4-20250514"},
APIKeyEnv: DefaultAPIKeyEnv,
}
}
```

**2. 在 `internal/provider/builtin/builtin.go` 中注册驱动:**

```go
import "neo-code/internal/provider/anthropic"

func Register(registry *provider.Registry) error {
if registry == nil {
return errors.New("builtin provider registry is nil")
}
if err := registry.Register(openai.Driver()); err != nil {
return err
}
return registry.Register(anthropic.Driver()) // 新增驱动注册
}
```

## 关键接口与类型

| 类型 | 位置 | 说明 |
|------|------|------|
| `Provider` 接口 | `internal/provider/provider.go` | 核心接口,定义 `Chat` 方法 |
| `ChatRequest` | `internal/provider/types.go` | 请求结构:`Model`、`SystemPrompt`、`Messages`、`Tools` |
| `ChatResponse` | `internal/provider/types.go` | 响应结构:`Message`、`FinishReason`、`Usage` |
| `StreamEvent` | `internal/provider/provider.go` | 流式事件:`text_delta` 和 `tool_call_start` |
| `ProviderConfig` | `internal/config/model.go` | 配置层:`Name`、`Driver`、`BaseURL`、`Model`、`Models`、`APIKeyEnv` |
| `DriverDefinition` | `internal/provider/registry.go` | 驱动层:`Name` + `Build` 构造函数 |
| `Registry` | `internal/provider/registry.go` | 驱动注册中心 |

## 设计约束

**必须遵守**:

- **API Key 只从环境变量读取**,不写入 `config.yaml`,不硬编码在源码中
- **驱动层不持有模型列表**,`Models` 完全由配置层(`ProviderConfig.Models`)控制
- **厂商差异收敛在 `internal/provider/` 内**,`runtime`、`tui` 等上层模块只依赖统一的 `Provider` 接口
- **`base_url` 不向用户暴露**,用户在 TUI 中只能看到 provider 名称和模型列表

## 相关文档

- [Provider Interface Target Design](../provider-interface-target-design.md)
- [Provider Module Interface](../provider-module-interface.md)
- [Provider Schema Strategy](../provider-schema-strategy.md)
Loading
Loading