<div align="center">
<h1>OY</h1>
<h3>AI 智能体工作区(CLI + TUI)</h3>
<p>
<a href="https://github.com/cherish-ltt/oy/actions/workflows/rust-ci.yml">
<img src="https://img.shields.io/github/actions/workflow/status/cherish-ltt/oy/rust-ci.yml?branch=main" alt="Build Status"/>
</a>
<a href="https://github.com/cherish-ltt/u2secure/blob/main/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license"/>
</a>
<a href="https://www.rust-lang.org">
<img src="https://img.shields.io/badge/rust-1.95.0+-orange.svg" alt="license"/>
</a>
</p>
</div>
> 🚀 一个 Rust 工作区,实现了基于 OpenRouter / 兼容 API 的函数调用 AI 智能体,提供 Read/Write/Edit/Bash 四类工具。基于**领域驱动设计**和**洋葱架构**原则,构建在四个隔离的 crate 之上。
## 架构
```
oy (CLI 入口, 默认启动 TUI) ────
├──► oy-agent (编排) ──► oy-ai (AI 提供者抽象)
oy (oy -p "prompt" 直接执行) ────
```
| [`oy-ai`](./oy-ai/) | 核心 | AI 提供者 trait、值对象(ChatMessage、ToolCall)、OpenRouter 实现 |
| [`oy-agent`](./oy-agent/) | 领域 | Agent 实体、Tool trait、编排器循环、工具实现(Read/Write/Edit/Bash)、会话持久化 |
| [`oy-code-cli`](./oy-code-cli/) | 基础设施 | CLI 参数解析(clap)、二进制入口 `oy` |
| [`oy-tui`](./oy-tui/) | 基础设施 | 基于 ratatui 的 TUI shell,markdown 渲染、命令系统、主题切换 |
### 依赖方向
- `oy-code-cli` → `oy-agent` → `oy-ai`
- `oy-tui` → `oy-agent` → `oy-ai`
- `oy-code-cli` → `oy-tui`(无 prompt 时启动 TUI)
## 快速开始
### 前置条件
- Rust 1.85+(`rust-toolchain.toml` 自动配置)
- 一个 OpenRouter 或兼容的 API 密钥
### 配置
`~/.oy-ai-agent/config.toml`:
```toml
api_key = "sk-or-..."
base_url = "https://openrouter.ai/api/v1"
model = "anthropic/claude-haiku-4.5"
theme = "light" # "light" 或 "dark"
```
也可通过环境变量设置:
```bash
export OPENROUTER_API_KEY="sk-or-..."
export OPENROUTER_BASE_URL="https://openrouter.ai/api/v1"
export OPENROUTER_MODEL="anthropic/claude-haiku-4.5"
```
### 安装与运行
```bash
# 从 crates.io 安装
cargo install oy-code-cli
# 启动 TUI(默认)
oy
# 或直接使用 CLI 模式
oy -p "当前目录下有哪些文件?"
```
### 本地开发
```bash
# 构建
cargo build --workspace
# 启动 TUI
cargo run -p oy-code-cli
# 测试
cargo test --workspace
```
## TUI 功能
### 命令系统
在输入框输入 `/` 弹出命令选择器:
| `/model` | 三步表单设置 API Base URL / API Key / Model,保存 config,重启 agent 且保留会话消息 |
| `/settings` → `/theme` | 切换 light / dark 主题,持久化到 config.toml |
快捷键:`↑`/`↓` 导航选择器,`Enter` 确认,`Esc` 取消,`Ctrl+O` 展开/折叠工具结果。
### Markdown 渲染
AI 回复内容以 Markdown 格式渲染,支持:
| `**bold**` | 加粗 |
| `*italic*` | 斜体 |
| `` `code` `` | 黑底青字 |
| ` ``` ` 代码块 | 独立段落,代码高亮 |
| `# Heading` | 标题 |
| `- list` | 列表 |
| `> quote` | 引用块 |
| `---` | 分割线 |
### 工具调用 & 结果显示
- 每个工具调用显示为 `🔧 工具调用 · Read`,含实时计时器
- 结果返回后合并显示在调用下方,带用时时长 `✓ (0.5s)`
- Read 结果折叠至前 5 行,Bash 结果折叠至后 5 行
- `Ctrl+O` 展开全部内容
### 主题
内置 light / dark 双主题:
- **light**:白底,消息气泡淡蓝/淡绿/淡紫
- **dark**:黑底,消息气泡深蓝/深绿/深紫
通过 `/settings` → `/theme` 切换,设置持久化到 config.toml。
### 消息背景色
每条消息独立背景色,整行填充:
| User | `Rgb(235,240,255)` 浅蓝 | `Rgb(25,30,50)` 深蓝 |
| Assistant | `Rgb(235,255,235)` 浅绿 | `Rgb(20,30,20)` 深绿 |
| Tool | `Rgb(255,235,255)` 浅紫 | `Rgb(40,20,40)` 深紫 |
### Agent 状态指示器
状态栏显示 agent 运行状态:`•` 空闲,`⠋⠙⠹…` 旋转动画表示工作中。
## 可用工具
| **Read** | 读取文件内容 | `file_path` |
| **Write** | 创建或覆盖文件 | `file_path`, `content` |
| **Edit** | 精确替换文件中的文本段 | `file_path`, `old_text`, `new_text` |
| **Bash** | 通过 `sh -c` 执行命令 | `command`(含命令黑名单) |
## 项目结构
```
oy/
├── Cargo.toml # 工作区
├── oy-ai/ # AI 提供者抽象
│ └── src/
│ ├── domain/ (AiProvider trait, ChatMessage, ToolCall, AiConfig, AiError)
│ └── infrastructure/ (OpenRouter 实现)
├── oy-agent/ # 智能体编排
│ ├── tests/integration_test.rs # MockProvider 集成测试
│ └── src/
│ ├── domain/ (Agent trait, Tool trait, ToolRegistry, AgentError)
│ ├── application/ (Orchestrator 主循环)
│ └── infrastructure/ (ReadTool, WriteTool, EditTool, BashTool, 持久化)
├── oy-code-cli/ # CLI 二进制 `oy`
│ └── src/ (CliArgs, run())
└── oy-tui/ # TUI 二进制
└── src/
├── app.rs # 应用状态、事件循环、键盘处理、命令执行
├── ui.rs # ratatui 渲染(消息区、输入框、状态栏、弹出框)
├── message.rs # Message 枚举、to_lines() MD 渲染、visual_line_count
├── command.rs # 命令注册器、CommandInfo、CommandItem
├── event.rs # 事件循环(tick、crossterm、agent 信号)
├── load_config.rs # config.toml 加载/保存
├── theme.rs # Theme 结构体、DARK_THEME / LIGHT_THEME
├── agent.rs # AgentManager
└── main.rs # 入口
```
## 测试
```
cargo test --workspace # 116 个测试
```
| oy-ai 单元 | `oy-ai/src/domain/` | 23 |
| oy-agent 单元 + 工具 | `oy-agent/src/` | 58 |
| oy-agent 集成 | `oy-agent/tests/` | 5 |
| oy-tui 单元 | `oy-tui/src/` | 30 |
## 会话持久化
对话历史自动保存到 `~/.oy-ai-agent/sessions/<项目路径>/<uuidv7>.json`。切换模型时(`/model` 命令),旧 agent 的上下文通过 `oneshot` channel 提取,注入到新 agent,确保会话不丢失。
## 许可证
MIT
---
<div align="center">
<sub>Built with ❤️ by the OY team</sub>
</div>