delphi-agent 是一个面向平台侧 Agent 后端的 Java / Spring Boot 框架。它提供了 ReAct Agent loop、会话运行时、工具系统、Subagent 编排、Redis 协调、MongoDB 持久化等核心能力,支持单节点和多节点部署,适用于构建各种基于 LLM 的 Agent 服务。
- ReAct Agent Loop:模型流式推理、工具调用、工具结果回灌、steering/follow-up、自动压缩。
- 会话运行时:MongoDB 持久化 session、entries、会话树、fork、navigate、compact、auto-retry。
- Subagent 编排:ORCHESTRATOR / PLANNER / RESEARCHER / REVIEWER / CODER / TESTER 角色分工,spawn/abort/status/result 工具链,配额守卫。
- 工具系统:内置文件/命令工具(read、bash、edit、write、grep、find、ls)、Skill 工具、Subagent 编排工具、TaskPlanning 工具、工具策略管道。
- Redis 协调:active run lease、fencing token、run queue、command channel、SSE pub/sub、rate limit。
- Run 生命周期:Mongo
runs记录ACCEPTED / QUEUED / RUNNING / COMPLETED / FAILED / ABORTED;terminal event 先落 Mongo outbox,Redis Stream append 成功后再把runs更新为终态并回填terminalEventId。 - SSE 可靠投递:事件先写 Redis Stream,SSE 使用 Redis Stream ID;客户端可用
Last-Event-ID连接任意节点补读。 - 正常下线闭环:Spring shutdown 自动 drain,拒绝新 run、停止 queue drainer、等待 active run、超时写
FAILED + failureType=SHUTDOWN_TIMEOUT。 - Workspace Snapshot:正式运行时只支持
snapshot存储,本地 workspace 只是执行热缓存,可清理、可重建。 - 多租户与治理:
tenantId == namespace校验、并发配额、速率限制、审计、用量统计、工具策略。 - Catalog 与 Skills:支持 namespace 级别的 Skill 可见性、指令型/可执行型 Skill、prompt catalog、resource catalog。
主要架构见 ARCHITECHE.MD,API / SDK 文档见 DOCS.md,数据存储结构见 DATA.md。
| 模块 | 职责 |
|---|---|
delphi-agent-ai-api |
模型 SPI(AiRuntime、ApiProvider、ApiProviderRegistry、ModelCatalog)、消息体系(Message sealed interface)、内容块(ContentBlock sealed interface)、工具定义(ToolDefinition、ToolCallContent、ToolResultMessage)、流式事件。 |
delphi-agent-springai-provider |
Spring AI 适配层:将 Spring AI ChatModel 适配为 ApiProvider,通过 PromptConverter 转换消息,FluxToEventStreamBridge 桥接流式响应。Reference server 通过 Anthropic 兼容接口接入 DeepSeek。 |
delphi-agent-core |
Agent loop(Agent.runLoop ReAct 循环)、AgentState、AgentTool 接口、ToolArgumentValidator 参数校验、AgentEvent sealed interface 事件模型、steering/follow-up 队列。 |
delphi-agent-runtime |
完整运行时:AgentRunRuntime(run 调度与执行)、AgentSessionRuntime(会话管理)、SessionCommandRuntime(compact/fork/navigate)、Redis 协调层(lease/queue/command/pubsub)、TerminalEventCommitService + Outbox、SnapshotWorkspaceStorage、SubagentRuntime、ToolInventory + ToolPolicyPipeline、TenantRuntimeGuard、Audit/Usage/Quota、NodeDrainManager + ShutdownDrainLifecycle、DelphiCodingAgentSdk。 |
delphi-agent-http-api |
HTTP 层:StreamChatController(/api/chat/stream)、SessionEventReplayController(/api/chat/events)、CatalogController、AuditController、UsageController、SessionEventBroker(SSE + Redis pub/sub fanout)、RuntimeIdentityResolver、Actuator 扩展(NodeInfoContributor、NodeDrainHealthIndicator)。 |
delphi-agent-server |
可运行 Spring Boot reference server:AiProviderConfiguration(注册 DeepSeek Anthropic 兼容端点)、默认 application.yml 配置、静态首页。 |
| 组件 | 版本 |
|---|---|
| Java | 21(Virtual Threads) |
| Spring Boot | 3.4.1 |
| Spring AI | 1.1.3 |
| MongoDB Driver | 5.1.2 |
| AWS SDK (S3) | 2.28.16 |
| Jackson | 2.17.2 |
| Maven | 3.9+ |
- JDK 21
- Maven 3.9+
- MongoDB 5+:正式运行时持久化 session / run / outbox;单节点可使用 standalone MongoDB
- Redis 6.2+
- S3-compatible object storage:例如 MinIO
- Docker Engine:正式 sandbox 默认使用 Docker backend
DEEPSEEK_API_KEY
本机如需切换 JDK,可使用项目环境中的 jdk21,或手动设置:
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
export PATH="$JAVA_HOME/bin:$PATH"# 编译
mvn -DskipTests compile
# 全量测试
mvn test
# 指定模块测试
mvn -pl delphi-agent-runtime test可以在项目根目录创建 .env:
PORT=8080
MONGODB_URI=mongodb://localhost:27017/pi-agent-framework
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
DEEPSEEK_API_KEY=sk-your-key
# 可选;不配置时会自动生成进程唯一 nodeId。多实例部署时显式设置为 pod/container 唯一值。
PI_NODE_ID=
PI_CLUSTER_REDIS_KEY_PREFIX=delphi
PI_CLUSTER_RUN_MAX_TTL_MS=1800000
PI_CLUSTER_LOCK_DEFAULT_TTL_MS=30000
PI_WORKSPACES_ROOT=./workspaces
PI_WORKSPACE_STORAGE=snapshot
PI_WORKSPACE_BUCKET=delphi-workspaces
PI_WORKSPACE_S3_ENDPOINT=http://localhost:9000
PI_WORKSPACE_S3_AK=minioadmin
PI_WORKSPACE_S3_SK=minioadmin
PI_WORKSPACE_S3_PATH_STYLE=true
PI_SHUTDOWN_DRAIN_TIMEOUT_SECONDS=1800
PI_SHUTDOWN_ABORT_GRACE_SECONDS=10
PI_AGENT_SKILLS_DIRS=./skills,${HOME}/.codex/skills
PI_AGENT_PROMPTS_DIRS=./prompts
PI_AGENT_RESOURCES_DIRS=./resources
PI_TOOLS_BUILTIN_ENABLED=true
PI_TOOL_POLICY_ORCHESTRATOR_STRICT=falsePI_CLUSTER_ENABLED 已废弃,不再是运行模式开关。旧字段如果仍出现在外部环境中,不影响正式 runtime bean 创建。
mvn -pl delphi-agent-server spring-boot:run默认服务地址:http://localhost:8080。
启动后可验证:
curl http://localhost:8080/actuator/health
curl http://localhost:8080/actuator/info
curl http://localhost:8080/api/catalog/models默认在 delphi-agent-server/src/main/resources/application.yml 中配置 DeepSeek(Anthropic 兼容端点):
pi:
models:
- id: deepseek-v4-pro
name: deepseek-v4-pro
api: spring-ai-deepseek
provider: deepseek
base-url: https://api.deepseek.com/anthropic
context-window: 1048576
max-tokens: 262144
- id: deepseek-v4-flash
name: deepseek-v4-flash
api: spring-ai-deepseek
provider: deepseek
base-url: https://api.deepseek.com/anthropic
context-window: 1048576
max-tokens: 262144
defaults:
provider: deepseek
model-id: deepseek-v4-pro
api-keys:
- DEEPSEEK_API_KEY接入新的模型只需要:
- 在
pi.models中追加条目,api字段指向已注册的ApiProvider。 - 注册自定义
ApiProvider到ApiProviderRegistry(参考SpringAiChatModelProvider)。 - 在
pi.api-keys中声明所需的环境变量名。
单节点不是另一套 standalone 代码路径,而是同一套 Redis + Mongo + snapshot runtime 的 replicas=1。扩展到多个节点后:
RedisLiveRunRegistry使用 Redis lease 保证同一 session 同时只有一个 active run。RunAdmissionController在获取 lease 时生成 fencing token。RedisRunQueueManager使用 Redis Stream 保存 queued run。RunQueueDrainer在非 draining 节点上扫描 queue,session 没有 active lease 时接力调度。RunCommandDispatcher通过 Redis command channel 把 abort/steer 发给 owner node。SessionEventBroker本地发送 SSE,同时通过 Redis pub/sub fanout 到其他节点。RuntimeEventStore把 runtime event 写入 Redis Stream,供 replay 和诊断。CacheInvalidationPublisher/CacheInvalidationSubscriber跨节点广播 catalog reload。
- 客户端请求
POST /api/chat/stream。 StreamChatController通过RuntimeIdentityResolver解析租户/用户/namespace,并检查NodeDrainManager。- draining 时拒绝新 prompt、continue、compact、fork、navigate;仅允许 abort/steer。
AgentRunRuntime写RunDocument(ACCEPTED),经过TenantRuntimeGuard校验和 queue policy。- 可运行时获取 Redis active lease,Mongo 更新
RUNNING;不可运行时写 Redis queue 并更新QUEUED。 AgentSessionRuntime.prompt/cont驱动Agent.runLoop,期间普通 event 先写 Redis Stream,再投递 SSE。- terminal event 通过
TerminalEventCommitService在 Mongo 事务内写入runtime_event_outbox(PENDING),outbox 中带完整终态元数据。 RuntimeEventOutboxWorkerappend Redis terminal event 成功后,调用RunLifecycleService.terminal(...)把runs更新为终态并写入terminalEventId,再释放 owner lease。- Redis append 失败时,outbox 进入
FAILED_RETRYABLE,lease 保留;后台 retry 成功后才释放。 - run 成功前必须完成 workspace snapshot;snapshot 失败会进入
run_failed,并阻止自动调度下一条 queued run。
实时接口是 POST /api/chat/stream。断线补读使用:
GET /api/chat/events?namespace=<ns>&sessionId=<session>&runId=<run>&lastEventId=<redis-stream-id>
服务端会先读取 Redis Stream high-watermark,再注册 live emitter,补发 (lastEventId, highWatermark],live 阶段只发送 > highWatermark 的事件,避免漏发和重复窗口。
正常停止进程时由 ShutdownDrainLifecycle 自动执行:
NodeDrainManager.draining = true。RunQueueDrainer停止消费新任务。AgentRunRuntime.shutdownForDrain(timeout)等待 active run 完成。- timeout 后 abort 本地 active run,短暂等待 abort 生效。
- 尽力持久化 workspace snapshot。
- 写
FAILED + failureType=SHUTDOWN_TIMEOUT或WORKSPACE_SNAPSHOT_FAILED_DURING_SHUTDOWN。 - terminal event 写入 Redis Stream 后释放 lease。
本闭环覆盖正常下线、正常重启和可控缩容,不承诺 kill -9、强制删除容器、OOM、断电等异常场景。
- API / SSE / 配置说明:DOCS.md
- 完整 API 参考:docs/api-reference.zh-CN.md
- SDK 使用手册:docs/agent-sdk.zh-CN.md
如果在同一个 Spring 容器内嵌入式接入,优先使用 delphi-agent-runtime 的 DelphiCodingAgentSdk,而不是绕一圈 HTTP/SSE:
@Autowired
DelphiCodingAgentSdk sdk;
AgentSessionHandle handle = sdk.createSession(
new CreateAgentSessionOptions("my-namespace", "my-project", "session-name", null, null, null)
);
handle.prompt("帮我写一个排序算法").join();
String answer = handle.lastAssistantText();所有命令型能力都通过 ExecutionBackend 进入 workspace:
ExecutionContext(namespace, sessionId, userId)
-> ExecutionBackend.execute/readFile/writeFile
-> workspaces/<namespace>/<sessionId>
正式 runtime 使用 SnapshotWorkspaceStorage:
prepareForRun()从 S3/MinIO 恢复 snapshot 到本地热目录。persistAfterRun()将 workspace 打包为snapshot.tar.gz上传对象存储。- 本地目录不是正式数据来源,可清理、可重建。
pi.execution.workspace-storage.type=local会启动失败。
DockerIsolatedBackend 默认配置:
--network none- 非 root 用户
- read-only rootfs
- 当前 session workspace 挂载
- memory / CPU / PIDs 限制(来自
pi.quota配置)
源码中保留的 LocalIsolatedBackend 只作为遗留/测试辅助,不作为正式 runtime 形态。
| 类别 | 说明 | 内置示例 |
|---|---|---|
READONLY |
只读,安全 | read、grep、find、ls |
MUTATING |
修改文件 | edit、write |
EXECUTABLE |
命令执行 | bash |
INSTRUCTIONAL |
指令型 Skill | 内嵌 prompt 类 Skill |
ORCHESTRATION |
编排 | subagent.spawn、subagent.abort、subagent.status、subagent.result、task.planning |
PI_TOOL_POLICY_ORCHESTRATOR_STRICT=true 时,主代理只能使用 READONLY / INSTRUCTIONAL / ORCHESTRATION,MUTATING / EXECUTABLE 必须通过 spawn CODER/TESTER subagent 完成。适合多租户生产环境的零信任架构。
ResourceCatalogService 启动时和 POST /api/catalog/reload 时扫描:
pi.resources.skills-dirs(默认./skills,${user.home}/.codex/skills)pi.resources.prompts-dirspi.resources.resources-dirs
Skill 目录结构:
skills/
├── public/ # 全 namespace 可见
│ └── <skill-name>/SKILL.md
└── namespaces/<namespace>/ # 仅该 namespace 可见
└── <skill-name>/SKILL.md
SKILL.md 头部 frontmatter 支持:description、args_schema、entrypoint、timeout_ms。带 entrypoint 的为可执行型,通过 sandbox 执行;不带 entrypoint 的为指令型,注入到 LLM 上下文。
| 能力 | 实现 |
|---|---|
| 租户隔离 | TenantRuntimeGuard 校验 tenantId == namespace,绑定到 RunContext。 |
| 并发配额 | TenantQuotaManager 解析 pi.quota.defaults 与 overrides,含并发会话、daily token、CPU、memory、pids 限制。 |
| 速率限制 | pi.rate-limit.default-rpm,Redis 滑动窗口。 |
| 审计 | AuditService 异步写 audit_logs,GET /api/audit 查询。 |
| 用量计量 | UsageMeteringService 周期性 flush 到 usage_metrics,GET /api/usage / GET /api/usage/today 查询。 |
| 工具策略 | ToolPolicyPipeline + TenantToolPolicyResolver + ToolExecutionWrapper + ToolAuditWrapper。 |
| Subagent 配额 | SubagentQuotaGuard 限制并发 subagent 数量与 max duration。 |
samples/ 目录下提供了 5 套端到端案例,覆盖 Skill、Sandbox、Multi-namespace、Multi-user、综合 SaaS 场景:
mvn -pl delphi-agent-server spring-boot:run
bash samples/1-basic-skill-usage/test.sh
bash samples/5-comprehensive-demo/test.sh详细列表与功能矩阵见 samples/README.md。
# 编译
mvn -DskipTests compile
# 全量测试
mvn test
# 指定模块测试
mvn -pl delphi-agent-runtime test
# 启动 reference server
mvn -pl delphi-agent-server spring-boot:run