一个类似 Claude Code / Codex 的 Coding Agent:理解任务、分析代码库、调用工具(读/写/改文件、搜索、执行命令)、运行测试并迭代直到完成任务。
当前进度:M0–M9(Streaming 实时输出 + Tool/SubAgent 增量展示 + 取消边界 + SubAgent 编排 + CLI 可观测性)已完成(516 个测试 + 5 个打包集成测试全绿),下一步为 M10+(MCP/SubAgent 深化等)。
git_status → read_file → edit_file → git_diff → git_commit → git_status → final已由AgentLoopGitCommitFlowTest以真实 Git + 9 Tool 闭环验证:真实文件修改、 commit 创建(git log 可见)、最终 status clean、tool_call_id 完整;COMMIT DENY 时 failure 回灌且 Agent 自纠不崩溃。
.\forgemind.cmd --working-dir D:\workspace --yes "检查 Git 修改,修复 src\Bug.java 的 bug,运行测试,查看 diff 并 git commit"| 模块 | 职责 |
|---|---|
agent-model |
纯数据模型:消息、Tool Call/Result、Schema、AgentResponse(含 finishReason)、ContextSummary(无业务依赖,仅 Jackson) |
agent-core |
核心编排:Agent / AgentLoop(畸形阈值/完整回灌/压缩+Summary/续写/Streaming 通道分派/取消边界)/ Tool SPI / Permission(READ/WRITE/SHELL/COMMIT)/ WorkspaceAccess / 异常 / ToolLimits / LlmConfig / LlmStreamClient SPI / ProgressListener 观察层 / ToolResultRenderer / ContextCompactor / TokenEstimator / DeterministicContextSummaryExtractor / RetryPolicy / Sleeper(仅依赖 model + slf4j,无 Spring) |
agent-llm |
FakeLlmClient + OpenAiCompatibleLlmClient(JDK HttpClient;finish_reason;SSE 流式解析 + 流式 Tool Call 累积;指数退避重试) |
agent-tools |
9 个 AgentTool:list_files / read_file / write_file / edit_file / search / shell / git_status / git_diff / git_commit(GitProvider 复用 ProcessRunner,COMMIT 独立权限) |
agent-cli |
picocli CLI + StreamingProgressRenderer 增量输出 + 日志脱敏 + YAML 配置 + shade fat jar + 闭环/集成测试 |
- 传输层变化,领域逻辑不变:
LlmStreamClient extends LlmClient;AgentLoop 检测到流式能力自动走stream(),否则回退chat()—— 两种模式完全兼容,chat()语义不退化。 - SSE 管线:
OpenAiSseParser(SSE→data)→OpenAiStreamAccumulator(data→增量+完整响应)→StreamToolCallAccumulator(tool_call 分片累积、arguments 一次性解析)。 - 实时增量输出(CLI 可观测性,M9.4 / P2.1):
StreamingProgressRenderer默认模式不展示 LLM 中间 assistant 文本(--verbose未接线,后续阶段提供);Tool 调用/结果按序号展示[1] list_files ✓/[2] shell ✗ exit=1(失败时附一行 stderr 摘要);SubAgent 生命周期显示[subagent:start] task [complete]/[failed];连续事件不产生空行;超长 SubAgent 任务截断。 - 最终摘要:每次任务结束输出
status: success / failed / cancelled、iterations/toolCalls/subAgents统计;最终答案永远由AgentResult.finalAnswer()完整输出且只输出一次(不再使用(streamed above)占位)。 - delta 不进 Context:AgentContext 只存完整 AssistantMessage(完整 content + 完整 tool_calls)与 ToolResult,tool_call_id 严格配对;增量仅供展示。
- 流式 Retry:仅 body 消费前重试(IO 失败 + 429/500/502/503/504);2xx 且 SSE 已开始绝不重试。
- 取消:线程中断 → AgentLoop 在循环边界返回
failed("cancelled");运行中的 Tool 不被打断(自然完成),后续不再调用 LLM。CLI 表现为status: cancelled。 - 安全链不变:流式完整 ToolCall 仍走
AgentLoop → ToolExecutor → PermissionManager → WorkspaceAccess → AgentTool;DENY 经流式通道 failure 回灌自纠。
- 模型:
sub_agent工具 +SubAgentFactory;主 Agent 经 ToolExecutor 调用工具 → 同步嵌套完整子 AgentLoop(同一线程,无并发)。 - 隔离:子 Agent 使用独立白名单 registry(⊆ 主工具集、强制排除
sub_agent,深度固定 1);子 Context 与主 Context 完全隔离;每个子工具调用仍完整经过ToolExecutor → PermissionManager → WorkspaceAccess → AgentTool。 - 限制:
maxSubAgents(默认 5,一次主 run 全局共享预算,超限 failure 回灌);子maxIterations优先于继承。 - 语义:子 Agent 失败/预算耗尽 →
[subagent:failed]回灌主 Agent,主 Agent 自纠继续;普通子失败不传播成 cancelled;线程中断 → 主/子均 cancelled。
- Token Budget:
contextMaxTokens(默认 100k,0=禁用回退字符预算)+contextReserveTokens(8k);ApproximateTokenEstimator(ASCII≈4c/token、CJK≈1.5c/token,近似非计费)。 - Context Summary:压缩旧消息组时由
DeterministicContextSummaryExtractor(不调 LLM)提取 task/modifiedFiles/commands/testResults,以 SYSTEM[CONTEXT SUMMARY]注入;原子组删除,tool_call_id 不孤裂。 - length 自动续写:
finish_reason=length→ 注入 continuation(maxContinuationAttempts默认 2);length+tool_calls 先执行工具。 - HTTP Retry:429/500/502/503/504 指数退避重试(500ms→5s ×2,jitter 可关);400/401/403/404/422 立即失败;错误不泄漏 API Key。
| 权限 | 默认 | 工具 |
|---|---|---|
| READ | ALLOW | read/list/search/git_status/git_diff |
| WRITE | ASK | write_file/edit_file |
| SHELL | ASK | shell |
| COMMIT | ASK | git_commit(git add -A + git commit -m,message 为独立进程参数,注入安全) |
--yes 仅表示 Answerer 恒允许,仍必经 ToolExecutor → PermissionManager → WorkspaceAccess → GitProvider → ProcessRunner。
- agent-model:36 · agent-core:141(含 SubAgent 工厂/规格 24、TokenEstimator 10、RetryPolicy 6、Summary 提取 7)
- agent-llm:97(含 SSE 解析 14 / 流式累积 27 / 流式客户端 15 / Fake 流式 5)
- agent-tools:111(含 git_status 6 / git_diff 11 / git_commit 14 / SubAgentTool 9)
- agent-cli:131(含 SubAgent 闭环 12 / SubAgent 隔离 6 / 渲染器 9 / CLI 可观测性 4 / 流式 CLI 5 / Continuation 8 / GitCommitFlow 2 / FinishReason 4)
$env:FORGEMIND_API_KEY = "sk-..." # 你的 OpenAI-Compatible Keymvn package # 生成 agent-cli\target\forgemind.jar# Windows 启动脚本(项目根)
.\forgemind.cmd # 交互 REPL(输入 exit 退出)
.\forgemind.cmd --config config\example.yml "分析当前项目"
.\forgemind.cmd --working-dir D:\workspace --yes "列出项目结构"
.\forgemind.cmd --yes "读取 README.md 并总结架构"
# 或直接 java -jar
java -jar agent-cli\target\forgemind.jar --help
java -jar agent-cli\target\forgemind.jar "单次任务"注意:本仓库所有自动化测试均使用 Fake LLM / 本地 Mock,不依赖真实 API Key。 真实 E2E 需你自己提供 Key,在独立沙箱
m5-e2e-workspace/中执行 (WorkspaceAccess 保证所有文件操作被限制在该目录内):
$env:FORGEMIND_API_KEY = "sk-..."
.\forgemind.cmd --config config\example.yml --working-dir m5-e2e-workspace --yes "读取 README.md,并总结项目当前架构"
.\forgemind.cmd --config config\example.yml --working-dir m5-e2e-workspace --yes "列出当前项目目录结构,并说明主要模块"
.\forgemind.cmd --config config\example.yml --working-dir m5-e2e-workspace --yes "读取 src\Test.java,并解释其作用"
.\forgemind.cmd --config config\example.yml --working-dir m5-e2e-workspace --yes "创建一个测试文件,然后读取它"
.\forgemind.cmd --config config\example.yml --working-dir m5-e2e-workspace --yes "修改测试文件中的指定文本"
.\forgemind.cmd --config config\example.yml --working-dir m5-e2e-workspace --yes "执行一个安全的 shell 命令,例如 dir"每次运行末尾输出 iterations / toolCalls 统计。
当前仓库状态:真实 LLM E2E 未执行(本环境未提供 API Key);Mock E2E 已通过(E2eWorkspaceMockTest 4 用例)。
| 模块 | 职责 |
|---|---|
agent-model |
纯数据模型:消息、Tool Call/Result、Schema(无业务依赖,仅 Jackson) |
agent-core |
核心编排:Agent / AgentLoop / Tool SPI / Permission / WorkspaceAccess / 异常 / ToolLimits / LlmConfig(仅依赖 model + slf4j,无 Spring) |
agent-llm |
FakeLlmClient(测试)+ OpenAiCompatibleLlmClient(JDK HttpClient,OpenAI/DeepSeek 兼容) |
agent-tools |
6 个 AgentTool 实现:list_files / read_file / write_file / edit_file / search / shell(含 cmd/powershell provider,UTF-8/GBK 双解码) |
agent-cli |
picocli CLI(单次任务 + REPL)+ 日志脱敏 + YAML 配置 + shade fat jar + 打包集成测试 |
llm:
baseUrl: https://api.deepseek.com # 或 OpenAI 官方
apiKey: ${FORGEMIND_API_KEY} # 仅环境变量注入
model: deepseek-chat要求:JDK 17+(编译目标 17)、Maven 3.9+。本机离线构建方式(本地仓库
D:\Program Files\maven-repo,需用 PowerShell 调用完整路径):
& 'D:\Program Files\Maven\bin\mvn.cmd' -o clean test
& 'D:\Program Files\Maven\bin\mvn.cmd' -o verify
& 'D:\Program Files\Maven\bin\mvn.cmd' -o package联网环境直接 mvn clean test / mvn verify / mvn package 即可。
- agent-model:26 个(数据模型、防御性拷贝、Jackson 序列化/反序列化)
- agent-core:56 个(WorkspaceAccess 17、ToolExecutor 12、Permission 9、Registry 6、 AgentLoop 5、ToolLimits 3、LlmConfig 3、DefaultAgent 1)
- agent-tools:71 个(list_files 16、read_file 11、search 10、edit_file 9、 write_file 7、shell 10(含中文 stdout/stderr)、executor 集成 8)
- agent-llm:31 个(FakeLlmClient 7 + OpenAiCompatibleLlmClient 24)
- agent-cli:61 个(闭环 26 + ConfigLoader 8 + 命令 5 + 装配 5 + 权限应答 6 + 日志脱敏 7 + Mock E2E 4)
- 打包集成测试(failsafe):5 个(jar 存在 / Main-Class /
java -jar --version/java -jar --help/ forgemind.cmd 存在) AgentLoop 5、ToolLimits 3、LlmConfig 3、DefaultAgent 1) - agent-tools:69 个(list_files 16、read_file 11、search 10、edit_file 9、 write_file 7、shell 8、executor 集成 8)
- agent-llm:31 个(FakeLlmClient 7 + OpenAiCompatibleLlmClient 24:请求/解析/401/429/500/ 超时/空 choices/非法 arguments/Authorization/tools schema)
- agent-cli:50 个(完整闭环 26 + ConfigLoader 8 + 命令 5 + 装配 5 + 权限应答 6)
- 覆盖重点:
../逃逸、绝对/跨盘符路径、符号链接逃逸(含经链接父目录写新文件)、 根目录写入拒绝、二进制/超大文件、edit 多匹配且失败不改原文件、search ignore 目录与结果上限、 shell exitCode/stdout/stderr/timeout(含进程树杀灭)/输出截断/工作目录、 LLM HTTP 错误/超时/畸形回灌自纠、CLI --yes/默认拒绝/单次任务/REPL
M0 骨架 → M1 数据模型+核心接口 → M2 六个 Tool → M3 AgentLoop 完整错误处理 → M4 真实 LLM + CLI → M5 打磨 → M6+ Git / MCP / SubAgent / RAG / Web UI / IDE Plugin