项目中具体Harness架构的实现

完整的Agent Harness由几个相互配合的核心能力层构成,它们共同决定了Agent的行为和性能:

  • 任务规划:通过内置的任务清单功能,将复杂目标分解为一系列可执行的子任务,并追踪每一步的执行状态。
  • 虚拟文件系统:为Agent提供一个内置的虚拟文件管理系统,支持读写、编辑、搜索等操作,使其能像人类开发者一样进行工作。
  • 子Agent委派:主Agent可以创建专门的“子Agent”来执行特定任务,实现任务隔离和上下文压缩。
  • 上下文管理:通过文件系统等策略,管理长对话中的Token消耗,防止因上下文溢出而导致性能下降。
  • 可插拔中间件:通过一组Hook函数,在模型和工具调用的关键节点注入自定义逻辑,实现缓存、重试、日志、PII检测等增强功能。
  • Skills技能系统:将提示词、文档、脚本等打包为可复用的标准模块,为Agent注入特定领域的知识和能力。并做到自我进化。
  • 持久化记忆:为Agent提供短期(对话历史)和长期记忆能力,使其能从历史交互中学习。

项目中Harness架构的核心能力(一):长周期复杂任务的Planning能力

提供 write_todos 工具,Agent 可维护结构化任务列表(pending/in_progress/completed),持久化在 Agent state 中。

项目实现

项目 说明
DeepAgents 内置 write_todos 工具由框架自动注册,无需显式配置
受益方 三个 Agent 全部受益:主 Agent 协调复杂任务、analyst 执行 5 步分析流程、order 执行 4 步订单操作
典型触发 analyst 启动时:”ls → 读 SKILL.md → 收集数据 → 分析 → 图表 → 报告”,框架自动建议拆为 todo
持久化 CHECKPOINTER(MongoDBSaver)持久化 Agent state,跨重启保留任务进度

项目中harness架构的核心能力(二):安全的和动态路由的文件系统

项目实现:通过 CompositeBackend 实现三层路由文件系统

路由分流效果

路径前缀 后端 生命周期 隔离粒度
/AGENTS.md OpenSandbox 沙箱生命周期 全局一份(只读)
/memories/{user_id}/ StoreBackend → MongoDB 跨会话永久 按 user_id 隔离
/persisted-skills/ StoreBackend → MongoDB 跨会话永久 按 namespace 组织
/skills/ OpenSandbox 沙箱生命周期 每次沙箱重建
/data/、/analysis/ OpenSandbox 沙箱生命周期 临时工作区
backend = lambda rt: CompositeBackend(
    default=sandbox_backend,           # OpenSandbox(临时文件、代码执行)
    routes={
        "/memories/": StoreBackend(    # 用户偏好 → MongoDB 持久化
            runtime=rt,
            namespace=lambda rt: (getattr(rt.runtime.context, 'user_id', 'default_user'),),
        ),
        "/persisted-skills/": StoreBackend(  # 持久化技能 → MongoDB
            runtime=rt,
            namespace=lambda rt: SKILLS_STORE_NAMESPACE,
        ),
    },
)

项目中harness架构的核心能力(三): Subagent的任务委派

主 Agent 拥有 task 工具,每次调用创建全新 Agent 实例,独立上下文,执行完返回单个报告。支持并行执行和特殊化配置。

项目实现

子 Agent 配置加载:loader.py

YAML 配置文件 (configs/*.yaml)
  │ load_subagent_configs()      ← YAML → dict 列表
  │ resolve_subagent_tools()     ← 工具名子串匹配 → 可调用对象
  │ _validate_subagent_config()  ← 必填字段校验
  ▼

两个子 Agent 对比

procurement-analyst procurement-order
YAML procurement_analyst.yaml procurement_order.yaml
description 含触发关键词(”分析”、”对比”、”报告”…) 含触发关键词(”下单”、”创建订单”、”修改”…)
工具数 8 个(6 MCP + web_search + generate_visualization) 5 个(3 MCP + web_search + request_order_info)
技能 scope /skills/procurement/ /skills/order/
中间件 Summarization + 调用限制(50/200) 调用限制(20/50)
interrupt_on order_create: approve/reject, order_update: approve/reject
context 隔离 独立上下文,analyst 长篇工具结果不污染主 Agent 独立上下文,订单操作不干扰分析对话

委派模板(AGENTS.md:74-121):

主 Agent 的 AGENTS.md 为每个子 Agent 定义了结构化的 task 调用模板,确保 user_id/username/偏好 正确传递到子 Agent。

项目中harness架构的核心能力(四):Context Management — 上下文管理

四种策略——Input context(启动时加载)、Compression(自动 offload + summarization)、Isolation(子 Agent 隔离)、Long-term memory(跨线程持久化)。

项目实现

a. Input Context

输入 来源 何时加载
system_prompt prompts.py Agent 创建时
AGENTS.md 本地 → 沙箱 /AGENTS.md(main_agent.py:122-124 Agent 创建时,memory 参数
用户偏好 /memories/{user_id}/preferences.md(StoreBackend) 每轮对话前 read_file
技能 frontmatter 各 SKILL.md 的 YAML frontmatter(渐进式披露) Agent 启动时扫描
用户上下文 ContextInjectionMiddleware 注入 SystemMessage 每轮 before_agent
子Agent报告路径/订单确认 委派结果 收到返回后

b. Compression

机制 实现 配置
自动 Offloading DeepAgents 内置,工具结果 > 20k tokens → 写入文件,上下文只保留路径+前10行预览 框架默认
自动 Summarization SummarizationMiddleware,上下文 85% 窗口 → 自动压缩历史为摘要,完整历史保存到 /conversation_history/ tools_summarization.py
手动 compact_conversation SummarizationToolMiddleware 注册的工具,Agent 在收到子Agent长篇报告/对话超6轮/用户频繁切换话题时主动调用 tools_summarization.py:40-42, model=deepseek-v4-flash

c. Isolation(子 Agent 上下文隔离)

analyst 执行 5 步分析流程时会产生大量中间结果(MCP 返回的供应商/零部件 JSON、Python 脚本输出、图表生成参数等),全部隔离在子 Agent 自己的上下文窗口中。主 Agent 只收到最终的结构化报告。

d. Long-term Memory

存储 后端 内容
用户偏好 StoreBackend → MongoDB /memories/{user_id}/preferences.md
持久化技能 StoreBackend → MongoDB /persisted-skills/ 下技能文件
对话状态 MongoDBSaver Checkpoint(HITL 中断恢复、跨重启对话)
对话历史 MongoDB(history.py 会话列表/消息历史

项目中harness架构的核心能力(五):Skills — 渐进式技能系统(自我进化)

遵循 Agent Skills 标准,每个技能是含 SKILL.md 的目录,支持渐进式披露——启动时只读 frontmatter,需要时再加载完整内容。

项目实现

Skills自我进化(4 阶段)

flowchart TD
    %% 样式定义
    classDef stage1 fill:#e3f2fd,stroke:#1565c0,stroke-width:2px;
    classDef stage2 fill:#e0f2f1,stroke:#00695c,stroke-width:2px;
    classDef stage3 fill:#fff3e0,stroke:#ef6c00,stroke-width:2px;
    classDef stage4 fill:#ede7f6,stroke:#5e35b1,stroke-width:2px;

    %% 阶段 1
    subgraph S1 ["阶段 1: 同步到沙箱"]
        direction TB
        Task1["<b>SkillsSyncMiddleware</b> (增量)<br>+ <b>_seed_files()</b> (首次)<br>路径: src/skills/ → /skills/{scope}/"]:::stage1
    end

    %% 阶段 2
    subgraph S2 ["阶段 2: 渐进式发现"]
        direction TB
        Task2["<b>目录扫描</b><br>ls /skills/procurement/<br>解析 SKILL.md Frontmatter"]:::stage2
        Task3["<b>按需加载</b><br>read_file() (仅需要时加载)"]:::stage2
    end

    %% 阶段 3
    subgraph S3 ["阶段 3: 运行时创建/下载"]
        direction TB
        Task4["<b>skill-management</b><br>execute(download_skill.py)"]:::stage3
        Task5["<b>部署与验证</b><br>解压到 /skills/main/ → 测试验证"]:::stage3
    end

    %% 阶段 4
    subgraph S4 ["阶段 4: 分配与持久化"]
        direction TB
        Task6["<b>分配</b><br>assign_skill(skill_name, agent_name)<br>复制到 /skills/{scope}/"]:::stage4
        Task7["<b>持久化</b><br>store.aput() (StoreBackend)"]:::stage4
        Task8["<b>清理与恢复</b><br>UserSkillsRestoreMiddleware & 清理压缩包"]:::stage4
    end

    %% 流程连线
    Task1 --> Task2
    Task2 --> Task3
    Task3 --> Task4
    Task4 --> Task5
    Task5 --> Task6
    Task6 --> Task7
    Task7 --> Task8

基于SkillsSyncMiddleware发现链

yaml system_prompt: "ls /skills/procurement/ 扫描可用技能"
  → 返回: chart_params.md, procurement-analysis/, supplier-price-urls/, web-scraper/
  → read_file("procurement-analysis/SKILL.md") 加载操作手册
  → 按流程激活各技能

项目中harness架构的核心能力(六): Human-in-the-Loop — 人工介入

通过 interrupt_on 参数选择性地在工具调用前暂停,等待人工审批或修改。

项目实现双层中断体系

第1层: 数据补充 (tool-internal interrupt)
  request_order_info 工具内部调用 interrupt()
  ├─ 触发时机: Schema校验发现必填字段缺失
  ├─ 恢复格式: {"supplement": "自由文本补充信息"}
  └─ 循环: 校验→补充→解析→校验...直到完整

第2层: 最终审批 (interrupt_on 配置)
  order_create / order_update 的 interrupt_on 配置
  ├─ 触发时机: 数据完整,准备执行
  ├─ 恢复格式: {"decisions": [{"type": "approve"|"reject"}]}
  └─ 特性: approve → 执行 / reject → 取消

代码位置

组件 位置 职责
interrupt_on 配置 procurement_order.yaml:30-34 声明哪些工具需审批
request_order_info hitl_tools.py 数据补充中断工具
SSE 中断检测 chat.py stream_mode=[“messages”,”values”],检测 values 中的 interrupts
前端恢复端点 chat.py POST /chat/{thread_id}/resume 接收 ResumeRequest → Command(resume=…)
前端中断横幅 InterruptBanner.vue 两种模式:数据补充输入框 vs HITL 审批卡片

两层中断互不干扰的原因request_order_info 不在 interrupt_on 列表中,不会被 HITL 中间件拦截。两者是顺序关系——先补齐数据,再审批执行。

项目中harness架构的核心能力(七):Memory — 用户偏好记忆管理

项目实现:以用户作用域来管理Memory

记忆层次

┌─ 用户层(读写,跨会话持久化)──────────────────────┐
│ /memories/{user_id}/preferences.md                 │
│ 来源: StoreBackend → MongoDB                      │
│ 内容: preferred_output, preferred_chart_type,      │
│       preferred_currency, preferred_language,      │
│       recent_suppliers, recent_queries             │
│ 加载: 每轮对话前 Agent 主动 read_file             │
│ 更新: Agent 手动 edit_file(偏好变更)             │
│       + MemoryUpdateMiddleware 自动维护            │
│         (recent_suppliers + recent_queries)        │
└───────────────────────────────────────────────────┘

记忆更新流程

【手动路径】用户说"以后都用饼图"
  → Agent edit_file /memories/{user_id}/preferences.md
  → 更新 preferred_chart_type: pie

【自动路径】对话结束后
  → MemoryUpdateMiddleware.aafter_agent()
  → LLM 提取 {suppliers: [...], query: "..."}
  → store.aput() 写入 StoreBackend
  → 跨会话保留

项目的最大安全问题: 基于OpenSandbox来解决

OpenSandbox 就是这个项目的唯一安全边界。所有 Agent 行为——运行 Python 脚本、执行 Shell 命令、读写文件、下载技能、爬取网页——全部发生在这个容器内部。OpenSandbox 的配置决定了这道墙有多厚。

综合风险评估

风险 等级 影响 当前状态
沙箱无网络出口限制 数据外传、内网探测、云凭证窃取 network_policy 解决
无 URL 白名单 (download_skill) 从恶意服务器下载 network_policy 解决
skill_name 路径穿越 assign_skill 访问任意沙箱路径 文件操作
user_id 隔离弱 多用户数据可被跨用户访问 文件操作
AGENTS.md 无技术只读 Agent 可能修改全局准则 防止文件修改
无边载配额/进程限制 沙箱资源耗尽 docker容器可以随时增加
download_sandbox_file 无敏感文件过滤 沙箱文件可外传到本地 无限制

OpenSandbox 在这里扮演的角色:它是最后的兜底线。即使Skills全部失守——恶意技能被下载、解压、恶意代码、文件操作——第 5 阶段执行时,恶意代码仍然被关在容器里。容器的网络策略决定了恶意代码能造成多大的外部影响。

Sandbox 做边界定义:墙内 vs 墙外

┌──────────────────────────────────────────────────────┐
│                    宿主机 (Windows)                    │
│                                                      │
│     FastAPI(Agent服务器)   MongoDB   MCP Server               │
│                                                      │
│  ┌────────────────────────────────────────────────┐  │
│  │          OpenSandbox 容器 (Linux)               │  │
│  │                                                │  │
│  │   /skills/        /data/       /analysis/      │  │
│  │   /memories/      /AGENTS.md   /workspace/     │  │
│  │                                                │  │
│  │   Python,Go,Java,Nodejs 环境                              │  │
│  │   execute("任意命令") → 在这里运行               │  │
│  │   web-scraper → 从这里发出 HTTP 请求            │  │
│  │   技能脚本 → 在这里被调用                        │  │
│  │                                                │  │
│  │   网络出口 ──────────────────────────────→ ???  │  │
│  └────────────────────────────────────────────────┘  │
│                                                      │
└──────────────────────────────────────────────────────┘

OpenSandbox 防护了三件事:

防护 机制
宿主机文件系统不可见 容器内 /skills/、/data/ 都是容器自己的文件系统,与宿主机 E:\my_project\ 物理隔离
进程隔离 execute() 启动的进程在容器内运行,无法直接操作宿主机进程
资源限制 cpu: “2”、memory: “4Gi” 防止容器耗尽宿主机资源
--- 本文结束 The End ---