用 Java 复刻 Claude Code:一个终端编程助手的实现
一个基于 LangChain4j 的智能编程助手🐕柯基 Code,能实现简易编程功能,复刻 Claude Code确实是我标题党啦......
1. 项目概述
KejiCode(柯基Code)是一个基于 LangChain4j 框架的简易智能终端编程助手。
- 自动化编程任务:创建文件、编辑代码、搜索内容
- 智能工具链:AI 自主决定调用哪些工具及调用顺序
- 安全可控:所有操作限制在指定工作目录内
- 多模型支持:适配 Claude、GPT-4、通义千问等主流 AI
下面是整个项目用到的一些技术栈
| 技术 | 版本 | 选型理由 |
|---|---|---|
| Java | 17 | 稳定、生态成熟、企业级支持 |
| LangChain4j | 0.35.0 | Java 生态最佳 AI 集成框架,原生支持工具调用 |
| JLine | 3.24.1 | 强大的终端交互库,支持命令补全、历史记录 |
| Jackson | 2.16.0 | 高性能 JSON/YAML 处理 |
| Maven | 3.x | 依赖管理、构建自动化 |
2. 功能展示
-
启动项目
-
常见命令
/help、/model、/version、/params temperature=0.5、/reset等,具体使用方式可参照/help -
进行第一轮对话,让它写一个"图书管理系统",这里用的模型是 qwen-max
请用纯java写一个图书管理系统,数据存存使用caffine,项目管理使用maven,可通过命令行对图书进行增删改查,项目目录为:D://eacape//keji-book-system终端输出内容
> 请用纯java写一个图书管理系统,数据存存使用caffine,项目管理使用maven,可通过命令行对图书进行增删改查,项目目录为:D://eacape//keji-book-system 处理用户消息: 请用纯java写一个图书管理系统,数据存存使用caffine,项目管理使用maven,可通过命令行对图书进行增删改查,项目目录为:D://eacape//keji-book-system ⚙️ 创建文件: D://eacape//keji-book-system/pom.xml 成功写入 1127 字节到 pom.xml ✅ 完成 ⚙️ 创建文件: D://eacape//keji-book-system/src/main/java/com/keji/book/Book.java 成功写入 1133 字节到 src\main\java\com\keji\book\Book.java ✅ 完成 ⚙️ 创建文件: D://eacape//keji-book-system/src/main/java/com/keji/book/BookManager.java 成功写入 3526 字节到 src\main\java\com\keji\book\BookManager.java ✅ 完成 ⚙️ 创建文件: D://eacape//keji-book-system/src/main/java/com/keji/book/Main.java 成功写入 176 字节到 src\main\java\com\keji\book\Main.java ✅ 完成 ⚙️ 创建文件: D://eacape//keji-book-system/.gitignore 成功写入 64 字节到 .gitignore ✅ 完成 省略好多日志.... ✨ 操作完成 项目已成功创建在 `D://eacape//keji-book-system` 目录下。这个简单的图书管理系统使用了 Caffeine 作为内存缓存来存储书籍数据,并通过 Maven 进行依赖管理。系统支持从命令行进行增删改查操作。 以下是项目的主要文件结构: - `pom.xml`: Maven 的配置文件,定义了项目的基本信息和依赖。 - `src/main/java/com/keji/book/Book.java`: 定义了书本类。 - `src/main/java/com/keji/book/BookManager.java`: 实现了图书管理功能,包括添加、删除、更新和列出所有书籍。 - `src/main/java/com/keji/book/Main.java`: 主程序入口,启动图书管理系统。 - `.gitignore`: 忽略编译后的文件和其他不需要纳入版本控制的文件。 要运行此项目,请确保您的环境已安装 Java 和 Maven。然后按照以下步骤操作: 1. 打开终端并导航到项目目录 `D://eacape//keji-book-system`。 2. 运行 `mvn compile` 命令以编译项目。 3. 运行 `mvn exec:java -Dexec.mainClass="com.keji.book.Main"` 启动应用程序。 现在你可以开始使用这个图书管理系统了!如果你有任何问题或需要进一步的功能,请告诉我。 -
可以在对应的目录成功的看到图书管理系统的代码
-
体验一下图书管理系统,简单的增删改查还是实现了呢~
-
上面看着交互还差一点,让柯基Code给优化下
> 能让输入更有仪式感吗?然后交互的文字提示用中文终端不输出
> 能让输入更有仪式感吗?然后交互的文字提示用中文 处理用户消息: 能让输入更有仪式感吗?然后交互的文字提示用中文 省略好多日志.... ⚙️ 创建文件: D://eacape//keji-book-system/src/main/java/com/keji/book/BookManager.java 成功写入 3884 字节到 src\main\java\com\keji\book\BookManager.java ✅ 完成 省略好多日志.... ✨ 操作完成 项目已更新,现在交互提示使用中文,并且输入和输出更有仪式感。以下是更新后的 `BookManager.java` 文件内容: ```java 省略好代码.... ``` 现在你可以按照以下步骤运行项目: 1. 打开终端并导航到项目目录 `D://eacape//keji-book-system`。 2. 运行 `mvn compile` 命令以编译项目。 3. 运行 `mvn exec:java -Dexec.mainClass="com.keji.book.Main"` 启动应用程序。 现在你可以开始使用这个更具仪式感的图书管理系统了!如果你有任何问题或需要进一步的功能,请告诉我。 -
重新体验图书管理系统
3. 系统架构设计
3.1 分层架构
KejiCode 采用经典的四层架构,每层职责清晰、低耦合高内聚
3.2 层次职责详解
3.2.1 表现层 (Presentation Layer)
核心组件:TerminalUI
职责:
- 输入监听:接收用户的文字命令
- 输出展示:格式化显示 AI 回复和工具执行结果
- 命令解析:处理特殊命令(
/help,/switch,/model等) - 用户体验:清晰的操作提示和状态反馈
设计亮点:
// 使用 JLine 提供类似 Shell 的交互体验
LineReader lineReader = LineReaderBuilder.builder()
.terminal(terminal)
.build();
// 支持命令补全、历史记录
String input = lineReader.readLine("\n> ");
比喻:这是柯基的"耳朵和嘴巴",负责听指令和说话。
3.2.2 应用层 (Application Layer)
核心组件 1:CodeAssistantAgent
这是整个系统的大脑,协调各个组件工作
核心方法设计:
// 1. 处理用户消息 - 入口方法
public String processUserMessage(String userMessage) {
// 添加用户消息到历史
conversationHistory.add(UserMessage.from(userMessage));
// 调用 AI 模型(传递工具规范)
Response<AiMessage> response = chatModel.generate(
conversationHistory,
getToolSpecifications() // 🔑 关键:告诉 AI 有哪些工具
);
// 检查 AI 的意图
if (response.hasToolExecutionRequests()) {
return handleToolExecution(response.toolExecutionRequests());
} else {
return response.text();
}
}
// 2. 工具链执行 - 核心逻辑
private String handleToolExecution(List<ToolExecutionRequest> requests) {
// 执行第一批工具
for (ToolExecutionRequest request : requests) {
executeSingleTool(request);
}
// 🔑 工具链循环:让 AI 持续决策
while (iteration < MAX_ITERATIONS) {
Response<AiMessage> followUp = chatModel.generate(
conversationHistory,
getToolSpecifications()
);
if (followUp.hasToolExecutionRequests()) {
// AI 还想调用更多工具
for (ToolExecutionRequest req : followUp.toolExecutionRequests()) {
executeSingleTool(req);
}
} else {
// AI 完成任务,返回最终回复
return followUp.text();
}
iteration++;
}
}
设计模式应用:
- 策略模式:不同 AI 模型的统一接口
- 观察者模式:工具执行结果通知
- 模板方法:工具执行的统一流程
核心组件 2:Configuration
配置管理采用分层配置策略:
配置优先级:命令行参数 > 环境变量 > YAML 配置 > 默认值
设计理由:
- 安全性:敏感信息(API Key)优先从环境变量读取
- 灵活性:不同环境可以有不同配置
- 便捷性:命令行参数方便临时调整
核心组件 3:PathValidator
路径验证是安全架构的基石,采用沙箱模式
核心算法:
public static Path validateAndNormalize(String targetPath) {
Path workDir = getWorkingDirectory();
// 1. 转换为绝对路径
Path target = Paths.get(targetPath).isAbsolute()
? Paths.get(targetPath)
: workDir.resolve(targetPath);
// 2. 规范化(核心:移除 .. 和 .)
target = target.normalize();
// 3. 边界检查(核心:startsWith)
if (!target.startsWith(workDir)) {
throw new SecurityException("路径越界");
}
return target;
}
安全保障:
- 防止路径遍历攻击(
../../../etc/passwd) - 防止符号链接逃逸(
link-to-/etc) - 防止绝对路径直达(
/tmp/malicious.sh)
3.2.3 领域层 (Domain Layer)
工具系统设计
所有工具实现统一的 Tool 接口:
public interface Tool {
String getName(); // 工具名称
String getDescription(); // 工具描述(给 AI 看)
ToolResult execute(String parameters) throws Exception;
}
工具分类:
| 类别 | 工具 | 作用 | 典型场景 |
|---|---|---|---|
| 文件操作 | Read | 读取文件内容(支持分页) | 查看代码、配置文件 |
| Write | 创建或覆盖文件 | 生成新文件、重写配置 | |
| Edit | 精确字符串替换编辑 | 修改代码片段、更新变量名 | |
| 搜索工具 | Glob | 按模式匹配文件名 | 查找所有 .java 文件 |
| Grep | 按正则搜索文件内容 | 查找所有 TODO 注释 | |
| 命令执行 | Bash | 执行任意 shell 命令 | 编译、运行、系统操作 |
| 版本控制 | Git | Git 操作封装 | 提交、推送、查看历史 |
| 构建工具 | Maven | Maven 操作封装 | 编译、打包、测试 |
| Npm | Npm 操作封装 | 安装依赖、运行脚本 |
工具注册机制
工具规范(Tool Specification)设计
这是告诉 AI "工具能做什么" 的关键:
ToolSpecification writeSpec = ToolSpecification.builder()
.name("Write")
.description("""
将内容写入文件。创建新文件或覆盖已有文件。
参数:
- file_path (必需): 文件的完整路径,如 "src/Main.java"
- content (必需): 要写入的文件内容
示例: {"file_path": "test.txt", "content": "Hello"}
""")
.build();
为什么需要详细的描述?
比喻:就像给新员工写岗位说明书
- 简单描述:"处理文件" → AI 不知道具体能做什么
- 详细描述:"创建或覆盖文件,需要 file_path 和 content 参数" → AI 清楚知道何时使用
模型工厂设计
采用抽象工厂模式 + 策略模式
工厂方法实现
public class AIModelFactory {
public static ChatLanguageModel createChatModel(
String provider,
String apiKey,
String modelName,
double temperature,
int maxTokens,
String customUrl,
String customHeaders
) {
return switch (provider.toLowerCase()) {
case "anthropic" -> buildAnthropicModel(apiKey, modelName, temperature, maxTokens);
case "openai" -> buildOpenAiModel(apiKey, modelName, temperature, maxTokens);
case "dashscope" -> buildDashScopeModel(apiKey, modelName, temperature, maxTokens);
case "deepseek" -> buildDeepSeekModel(apiKey, modelName, temperature, maxTokens);
case "custom" -> buildCustomModel(apiKey, modelName, temperature, maxTokens, customUrl, customHeaders);
default -> throw new IllegalArgumentException("不支持的提供商: " + provider);
};
}
private static ChatLanguageModel buildAnthropicModel(...) {
return AnthropicChatModel.builder()
.apiKey(apiKey)
.modelName(modelName)
.temperature(temperature)
.maxTokens(maxTokens)
.build();
}
// ... 其他模型构建方法
}
对话历史管理
对话历史是 AI 的"记忆",设计要点
消息类型:
- UserMessage:用户输入
- AiMessage:AI 回复(文字或工具调用请求)
- ToolExecutionResultMessage:工具执行结果
历史管理策略:
public class ConversationHistory {
private final List<ChatMessage> messages;
private final int maxHistorySize; // 防止内存溢出
public void add(ChatMessage message) {
messages.add(message);
// 策略1:限制总消息数
if (messages.size() > maxHistorySize) {
// 保留最近的消息
messages.subList(0, messages.size() - maxHistorySize).clear();
}
// 策略2:智能压缩(未来优化)
// - 保留完整的最近 N 轮对话
// - 较早的对话只保留摘要
}
}
设计考虑:
- 上下文连贯性:AI 需要记住之前的对话才能正确理解
- 内存管理:过长的历史会占用大量内存
- 成本控制:AI API 按 token 收费,历史越长成本越高
3.2.4 基础设施层 (Infrastructure Layer)
这一层封装了所有第三方依赖和系统调用:
核心职责:
- AI 服务对接:通过 LangChain4j 调用各家 AI API
- 文件系统操作:Java NIO 文件读写
- 进程管理:ProcessBuilder 执行外部命令
- 日志记录:SLF4J + Logback
为什么需要这一层?
比喻:就像建筑的地基,上层不用关心底层细节
// ❌ 不好的设计:直接调用
Files.writeString(Paths.get("file.txt"), "content");
// ✅ 好的设计:通过抽象层
fileSystemService.writeFile("file.txt", "content");
优势:
- 易于测试:可以 mock 文件系统
- 易于替换:底层实现变化不影响上层
- 统一控制:在这一层统一处理异常、日志等
4. 对话流程设计
4.1 完整交互流程
这是 KejiCode 最核心的流程设计
4.2 工具链决策机制
AI 如何决定调用哪些工具?这是一个智能决策过程
决策影响因素:
-
System Prompt:定义 AI 的行为规范
"当用户说'创建文件'时,使用 Write 工具" "当用户说'查找'时,使用 Glob 或 Grep 工具" -
工具描述:详细说明工具用途和参数
"Write: 创建新文件或覆盖已有文件,参数..." -
对话历史:上下文决定下一步操作
用户: "创建文件" AI: [创建文件] 用户: "编辑它" ← AI 知道"它"指的是刚创建的文件
4.3 错误恢复流程
当工具执行失败时,如何处理?
错误处理策略:
public ToolResult execute(String parameters) {
try {
// 1. 参数验证
validateParameters(parameters);
// 2. 安全检查
PathValidator.validateAndNormalize(filePath);
// 3. 执行操作
performOperation();
return ToolResult.success("操作成功");
} catch (SecurityException e) {
// 安全错误:清晰提示边界
return ToolResult.failure("安全错误: " + e.getMessage());
} catch (IllegalArgumentException e) {
// 参数错误:提示正确用法
return ToolResult.failure("参数错误: " + e.getMessage() + "\n使用说明: ...");
} catch (IOException e) {
// IO 错误:提示具体问题
return ToolResult.failure("IO 错误: " + e.getMessage());
} catch (Exception e) {
// 未知错误:记录日志
log.error("工具执行异常", e);
return ToolResult.failure("执行失败: " + e.getMessage());
}
}
结语
KejiCode 可以帮你简单了解Coding Agent的工作原理,Happy Coding! 🐕💻
代码可以通过关注微信公众号【柯基写代码】回复【柯基Code】获取~
