1. 这不是“套娃”,是Agent系统工程的成熟信号
最近在几个技术社群里,总有人把“Agent 编排 Agent”当成一个玄学概念——好像只要让一个Agent去调另一个Agent,就自动升级成了“高阶智能体”。但真正用过 DeepSeek Harness 的人知道,这背后根本不是简单的嵌套调用,而是一整套可验证、可调试、可灰度发布的子代理(subagent)协同机制和工作流(workflow)治理框架。我从去年底开始在三个真实业务线落地Harness,从客服知识库自动归因、到内部IT工单的多角色协同处理、再到合规审计报告的跨系统数据拉取与校验,核心驱动力就是它对 subagent 的抽象设计:每个子代理不是孤立的函数封装,而是具备独立上下文生命周期、技能边界声明、失败回滚契约、可观测性埋点的运行单元。比如我们部署在内网的工单处理 workflow,主 agent 负责流程调度与状态聚合,而 subagent 分别承担“解析邮件正文”、“查询CMDB资产信息”、“调用审批API”、“生成审计日志”四项职责——它们之间不共享内存,不直连数据库,全部通过 Harness 内置的Seam 消息总线进行结构化通信。这种设计直接规避了传统微服务架构中常见的循环依赖、版本错配、链路追踪断裂等问题。更关键的是,Harness 的 workflow 编排不是靠 YAML 写死路径,而是基于 runtime 的动态拓扑发现:当某个 subagent 因权限变更临时下线时,主 agent 会自动触发 fallback 策略,降级调用备用技能模块,整个过程对上层业务无感。这已经超出了“AI 工具”的范畴,接近一个轻量级的分布式任务调度平台。如果你还在用 Python 脚本硬编码 Agent 调用链,或者靠 LangChain 的 Chain 类强行拼接逻辑,那真的该重新审视下底层架构了——不是所有“编排”都叫 workflow,也不是所有“子代理”都能构成生产级系统。
2. 子代理(subagent)不是功能拆分,是责任边界的硬性隔离
2.1 为什么必须定义 subagent?——从一次线上事故说起
去年11月,我们在金融风控场景上线了一个贷款反欺诈 workflow。初期版本把“提取身份证OCR字段”、“比对公安库”、“计算信用分”、“生成风险报告”全塞进一个大 Agent 里。结果某天公安库接口响应延迟飙升到8秒,整个 workflow 卡死,下游所有请求堆积,监控告警疯狂刷屏。复盘时发现,问题根源在于没有做责任隔离:OCR 解析本身只需200ms,却要为公安库的慢响应陪绑。后来我们按 Harness 的 subagent 规范重构,将四个能力拆成独立 subagent,并强制约定:
- 每个 subagent 必须声明SLA 承诺(如 OCR subagent 声明 P99 ≤ 300ms)
- 必须提供健康检查端点(/health 返回 {“status”: “ok”, “latency_ms”: 124})
- 必须实现幂等执行契约(相同 input_id 下多次调用返回相同 output_id)
重构后,当公安库 subagent 健康检查失败时,主 workflow 自动跳过该节点,用历史缓存数据生成降级报告,同时触发告警通知运维团队修复。整个过程耗时从平均12秒降到1.7秒,错误率下降92%。这说明 subagent 的本质不是代码组织方式,而是服务契约的显式化表达——它把模糊的“这个功能应该快一点”转化成可测量、可监控、可熔断的工程约束。
2.2 subagent 的三大硬性约束与实现原理
Harness 对 subagent 的约束不是空谈,而是通过 Rust 运行时强制实施的。我翻过它的 core/src/subagent.rs 源码,核心机制如下:
第一,上下文隔离(Context Isolation)
每个 subagent 启动时都会获得一个独立的ContextHandle,它封装了:
- 专属的 tokio Runtime 实例(避免 CPU 密集型 subagent 抢占 IO 线程)
- 隔离的内存池(默认 64MB,超限自动 OOM 杀死进程,防止内存泄漏拖垮整个 Harness)
- 独立的环境变量沙箱(如 DATABASE_URL 只对当前 subagent 可见)
提示:不要试图在 subagent 里读取全局配置文件。Harness 的设计哲学是“配置即代码”,所有参数必须通过 workflow 定义中的
input_schema显式注入。比如 OCR subagent 的confidence_threshold参数,必须在 workflow YAML 中声明:- name: ocr_processor type: subagent config: confidence_threshold: 0.85
第二,技能边界声明(Skill Boundary Declaration)
Harness 要求每个 subagent 在注册时提交Skill Manifest,这是一个 JSON Schema 文件,定义:
- 输入字段名、类型、是否必填(如
{"id_card_image": {"type": "string", "format": "base64"}}) - 输出字段名、类型、业务语义(如
{"id_number": {"type": "string", "pattern": "^\\d{18}$"}}) - 依赖的外部服务列表(如
["ocr-api.internal", "redis-cache"])
这个 manifest 不是文档,而是运行时校验依据。当主 agent 调用 subagent 时,Harness 会先做 schema 校验:如果传入的 base64 字符串长度超过 10MB,直接返回 400 错误,不会进入 subagent 进程。我们曾用这个机制拦截了 73% 的恶意构造请求——攻击者试图用超长字符串触发 OCR 模块的 buffer overflow,但在到达 subagent 之前就被 Harness 拦截了。
第三,失败回滚契约(Failure Rollback Contract)
这是最体现 Harness 工程深度的设计。每个 subagent 必须实现rollback()方法,且该方法必须满足:
- 幂等性(多次调用效果相同)
- 无副作用(不能修改外部状态)
- 超时严格控制在 200ms 内(Harness 内置 watchdog 强制 kill)
以我们的支付风控 workflow 为例:当“扣减账户余额” subagent 执行成功,但后续“发送短信通知” subagent 失败时,Harness 不会简单重试,而是立即调用“扣减余额” subagent 的 rollback 方法,执行“增加余额”操作。这个 rollback 不是事务回滚,而是业务层面的补偿动作——它要求开发者在写 subagent 时,就必须同步设计正向操作与逆向操作,从根本上杜绝“半成品状态”。
2.3 subagent 与传统微服务的关键差异
很多人问:“subagent 和微服务有啥区别?” 我画了个对比表,这是我们在技术评审会上用的真实数据:
| 维度 | 传统微服务 | Harness subagent | 我们的实测差异 |
|---|---|---|---|
| 启动时间 | 平均 3.2s(Spring Boot) | 平均 142ms(Rust + static linking) | subagent 冷启动快 22 倍,适合短时 burst 流量 |
| 内存占用 | 420MB(JVM 堆+元空间) | 18MB(静态链接二进制) | 单节点可部署 23 倍数量的 subagent |
| 调用开销 | HTTP + JSON 序列化 ≈ 8.7ms | Seam 总线 + bincode 序列化 ≈ 0.3ms | 端到端延迟降低 96%,对 latency 敏感场景至关重要 |
| 版本管理 | 需要 Service Mesh 控制流量比例 | workflow 定义中直接指定 subagent 版本号(如v2.3.1) | 灰度发布无需改 infra,只需更新 workflow YAML |
| 安全边界 | 依赖网络策略(NetworkPolicy) | 默认禁用所有外网访问,仅允许声明的 service name | 防止 subagent 逃逸到公网,满足金融级安全审计 |
特别强调一点:subagent 的“轻量”不是牺牲功能换来的。我们用harness-subagent-sdk开发的 PDF 解析 subagent,集成了 poppler、pdfium、tesseract 三个 C 库,编译后二进制 42MB,但启动后 RSS 内存稳定在 18MB——Rust 的零成本抽象在这里体现得淋漓尽致。而 Java 微服务即使只做同样功能,JVM 自身就要吃掉 200MB 内存。
3. Workflow 编排不是画流程图,是定义状态机与契约网络
3.1 Harness workflow 的三层抽象模型
很多团队第一次接触 Harness workflow 时,会下意识打开 VS Code 画 BPMN 图。但 Harness 的设计完全反其道而行之——它不让你画图,而是逼你用代码思维定义状态迁移规则。整个 workflow 模型分为三层:
第一层:State Schema(状态模式)
每个 workflow 必须定义一个state.json,描述整个流程的合法状态集合及转换条件。例如我们的合同审核 workflow:
{ "initial_state": "draft", "states": { "draft": { "allowed_transitions": ["submitted"] }, "submitted": { "allowed_transitions": ["approved", "rejected", "revised"] }, "approved": { "final": true }, "rejected": { "final": true } } }这个 schema 不是装饰,而是运行时强制校验。当某个 subagent 尝试将状态从submitted直接跳到approved时,Harness 会拒绝该 transition,并记录 audit log。我们靠这个机制堵住了 3 次人为绕过审批流程的尝试——业务方以为改个 API 参数就能跳过法务审核,结果被 Harness 拦在了状态机门外。
第二层:Transition Logic(迁移逻辑)
状态转换不是自动发生的,必须由 subagent 显式触发。每个 subagent 的输出必须包含next_state字段,且该字段值必须在 state.json 的 allowed_transitions 列表中。比如“法务审核” subagent 的输出:
{ "decision": "approve", "comments": "条款符合最新监管要求", "next_state": "approved" }Harness 会校验next_state是否合法,再执行状态变更。这种设计让业务逻辑变得极其清晰:谁负责哪个状态?什么条件下能进入下一个状态?全部白纸黑字写在代码里,而不是藏在某个 if-else 分支中。
第三层:Contract Network(契约网络)
这才是 Harness workflow 最颠覆性的创新。它把 subagent 之间的依赖关系,从“调用链”升级为“契约网络”。每个 subagent 在 manifest 中声明的dependencies,会被 Harness 构建成一个有向无环图(DAG),但这个图不是用来决定执行顺序的,而是用来验证契约履行情况的。例如:
- OCR subagent 声明依赖
id_card_image字段 - 公安库 subagent 声明依赖
id_number字段 - 当 workflow 运行时,Harness 会检查:
id_number是否由 OCR subagent 输出?如果不是,直接报错ContractViolation: id_number not produced by declared producer
我们曾用这个机制发现了一个隐藏三年的 bug:某个旧版 workflow 里,公安库 subagent 的输入字段id_number实际来自前端直传,而非 OCR 解析结果。这导致当 OCR 识别错误时,公安库永远查不到真实身份证号。Harness 上线后,这个契约校验立刻暴露了问题,我们花了两天就修复了数据流。
3.2 动态拓扑发现:让 workflow 拥有“自愈”能力
传统 workflow 引擎(如 Airflow、Camunda)的 DAG 是静态的,一旦定义就无法更改。Harness 则不同,它的 workflow 在 runtime 会进行动态拓扑发现。具体怎么运作?
当 Harness 启动时,它会扫描所有已注册的 subagent manifest,构建一个全局的Capability Registry。这个 registry 记录了:
- 每个 subagent 能处理的 input schema(如
{"id_card_image": "string"}) - 每个 subagent 能产生的 output schema(如
{"id_number": "string"}) - 每个 subagent 的健康状态(来自 /health 接口)
当一个 workflow 被触发时,Harness 不是按 YAML 顺序执行,而是:
- 解析 workflow 的初始 input
- 查询 Capability Registry,找出所有能消费该 input 的 subagent
- 根据 manifest 中的
priority字段(默认 0,可设为 100)选择最优 subagent - 执行该 subagent,获取 output
- 重复步骤 2-4,直到达到 final state 或无可用 subagent
这个机制带来的好处是惊人的。去年我们遇到一个极端案例:OCR subagent 因 GPU 驱动问题崩溃,健康检查持续失败。按传统方案,整个 workflow 会卡死。但 Harness 自动切换到了备用的 CPU 版 OCR subagent(priority 设为 90),虽然识别速度慢了 3 倍,但保证了业务连续性。更妙的是,当 GPU 驱动修复后,Harness 在下次 health check 通过时,自动切回高性能版本——整个过程无需人工干预,也不需要改任何 workflow 定义。
注意:动态拓扑发现不是万能的。它要求所有 subagent 的 input/output schema 必须严格兼容。我们吃过亏:某次升级 OCR subagent,把
id_number字段类型从string改成了object(含校验码),结果所有依赖它的 subagent 都报 schema mismatch 错误。教训是:schema 变更是 breaking change,必须遵循语义化版本规范,且在 manifest 中明确标注breaking_changes: ["id_number type changed"]。
3.3 Seam 总线:不是消息队列,是契约执行引擎
提到 Harness 的通信机制,很多人第一反应是“是不是用了 Kafka 或 RabbitMQ?” 答案是否定的。Seam 是 Harness 自研的轻量级契约执行总线,它只有 3 个核心能力:
1. Schema-aware routing(模式感知路由)
Seam 不转发原始 payload,而是先解析 JSON,提取output_schema中声明的字段,再按需投递。比如 OCR subagent 输出:
{ "id_number": "11010119900307281X", "name": "张三", "address": "北京市东城区...", "confidence": 0.92 }但公安库 subagent 的 manifest 只声明需要id_number字段,那么 Seam 只会把{"id_number": "11010119900307281X"}这个精简对象发给它,其他字段被自动过滤。这减少了 68% 的网络传输量,也杜绝了 subagent 误读无关字段的风险。
2. Contract enforcement(契约强制执行)
每个 subagent 注册时,Seam 会为其生成一个Contract Proxy。当主 agent 调用seam_call("ocr_processor", input)时,实际调用的是这个 proxy,它会:
- 校验 input 是否符合 manifest 中的 input_schema
- 校验 subagent 进程是否 healthy
- 记录调用耗时、成功率、错误码
- 如果 subagent 返回的 output 不符合 output_schema,proxy 直接返回 500 错误,不向上层透传脏数据
3. Cross-process consistency(跨进程一致性)
这是 Seam 最难被理解但价值最高的特性。它保证:同一个 workflow 实例的所有 subagent 调用,共享同一个Consistency Token。这个 token 是一个 cryptographically secure random string,随 workflow 启动时生成,贯穿整个生命周期。每个 subagent 在处理时,都可以访问这个 token,并用于:
- 生成幂等 ID(如
order_id = sha256(token + "create_order")) - 关联分布式 trace(所有 subagent 的日志都带
trace_id=token) - 触发跨 subagent 的补偿操作(如
rollback_token = token + "_rollback")
我们用这个机制实现了“跨系统事务一致性”。比如在电商 workflow 中,“创建订单” subagent 和“扣减库存” subagent 可能部署在不同物理机上,但它们通过同一个 Consistency Token 关联,当库存扣减失败时,订单创建 subagent 能精准定位到本次 workflow 实例,执行精确回滚,而不是盲目取消所有订单。
4. 生产级落地:从安装到内网部署的完整实操链
4.1 DeepSeek Harness 的安装不是“一键部署”,是环境契约签署
网上很多教程说“curl -sL https://get.harness.deepseek.ai | bash就完事了”,这是严重误导。Harness 的安装本质是签署一份环境契约,它会严格校验你的系统是否满足生产要求。我整理了我们团队踩过的所有坑:
第一步:硬件与内核校验
Harness 安装脚本会执行:
# 检查 CPU 是否支持 AVX2(Rust 依赖) grep -q avx2 /proc/cpuinfo || { echo "AVX2 required"; exit 1; } # 检查内核版本(必须 ≥ 5.4,因使用 io_uring) uname -r | grep -E '^(5\.[4-9]|[6-9]\.)' || { echo "Kernel too old"; exit 1; } # 检查 cgroups v2 是否启用(Rust async runtime 依赖) mount | grep -q "cgroup2.*rw" || { echo "cgroups v2 required"; exit 1; }我们曾在一台 CentOS 7 服务器上卡在这一步——内核是 3.10,无论如何升级都达不到要求。最终方案是:用 Docker 启动一个 Ubuntu 22.04 容器,在容器内运行 Harness。注意:必须用--cgroup-parent指定 cgroups v2 路径,否则 Harness 会报io_uring setup failed。
第二步:证书与密钥初始化
Harness 默认启用 mTLS,安装时会生成:
ca.crt:根证书(用于验证所有 subagent 证书)harness-server.key/crt:服务端证书subagent-template.key/crt:subagent 证书模板
关键经验:不要用自签名证书应付。我们最初为了省事,用 OpenSSL 生成了自签名 CA,结果在内网部署时,所有 subagent 都报
x509: certificate signed by unknown authority。原因是 Harness 的 Rust TLS 库(rustls)默认不信任自签名根证书。正确做法是:用step-ca搭建私有 CA,将 root cert 加入系统 trust store,再用step-ca签发 Harness 证书。整个过程耗时 4 小时,但换来的是真正的零信任安全。
第三步:Seam 总线初始化
这一步最容易被忽略,但决定了 workflow 能否跑起来。Harness 会启动一个seam-broker进程,它需要:
- 一个持久化存储(默认 SQLite,生产环境必须换为 PostgreSQL)
- 一个 Redis 实例(用于分布式锁和 pub/sub)
我们犯的最大错误是:在内网服务器上只装了 Redis,没装 PostgreSQL,结果 Harness 启动后,workflow 一直卡在pending状态。查日志才发现seam-broker报错failed to connect to postgresql://...。解决方案:修改harness.yaml,把seam.storage.type设为redis(它支持用 Redis Stream 替代 PostgreSQL),但要注意 Redis 内存必须 ≥ 2GB,否则 Stream 溢出会导致消息丢失。
4.2 subagent 开发:从 Rust SDK 到技能部署的全流程
Harness 官方推荐用 Rust 开发 subagent,但我们也成功用 Python(通过 PyO3 绑定)和 Go(通过 cgo)开发了部分模块。以下是 Rust SDK 的标准流程:
1. 创建项目骨架
cargo new --bin my-ocr-subagent cd my-ocr-subagent # 添加 harness-subagent-sdk 依赖 echo 'harness-subagent-sdk = { git = "https://github.com/deepseek-ai/harness-rs", tag = "v0.8.2" }' >> Cargo.toml2. 实现核心 trait
每个 subagent 必须实现Subagenttrait:
use harness_subagent_sdk::{Subagent, Input, Output, Result}; struct OcrSubagent; #[async_trait::async_trait] impl Subagent for OcrSubagent { // 声明输入输出 schema fn input_schema(&self) -> serde_json::Value { json!({ "id_card_image": { "type": "string", "format": "base64" } }) } fn output_schema(&self) -> serde_json::Value { json!({ "id_number": { "type": "string", "pattern": "^\\d{18}$" } }) } // 主处理逻辑 async fn execute(&self, input: Input) -> Result<Output> { let image_data = base64::decode(input.get_str("id_card_image")?)?; let text = tesseract::recognize(&image_data)?; Ok(json!({ "id_number": extract_id_number(&text) })) } // 补偿逻辑 async fn rollback(&self, _input: Input, _output: Output) -> Result<()> { // OCR 无副作用,rollback 为空操作 Ok(()) } } #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { OcrSubagent.run().await?; Ok(()) }3. 构建与签名
Harness 要求所有 subagent 二进制必须用私钥签名,防止篡改:
# 生成 subagent 私钥 openssl genpkey -algorithm RSA -out subagent.key -pkeyopt rsa_keygen_bits:2048 # 构建 release 版本 cargo build --release # 签名二进制 harness-cli sign --key subagent.key --output my-ocr-subagent.sig target/release/my-ocr-subagent4. 部署到内网服务器
内网部署的关键是证书链同步。我们总结了四步法:
- 将
ca.crt、subagent-template.crt、subagent-template.key复制到内网服务器/etc/harness/certs/ - 修改 subagent 的
Cargo.toml,添加证书路径:[dependencies.harness-subagent-sdk] version = "0.8.2" features = ["cert-path=/etc/harness/certs/ca.crt"] - 构建时指定证书:
cargo build --release --features "cert-path=/etc/harness/certs/ca.crt" - 启动时挂载证书目录:
./my-ocr-subagent --cert-dir /etc/harness/certs/
实操心得:内网部署最大的坑是 DNS。Harness 默认用 service name(如
ocr-subagent.default.svc.cluster.local)做服务发现,但内网服务器通常没有 Kubernetes DNS。解决方案是:在/etc/hosts中手动映射,或修改harness.yaml的seam.dns_mode为static,并配置seam.static_hosts列表。
4.3 workflow 编排实战:一个可落地的金融风控案例
我们为某银行开发的“贷款申请实时风控 workflow”,完整展示了 Harness 的能力。以下是可直接复用的 YAML 定义:
# risk-workflow.yaml name: loan_risk_assessment version: "1.2.0" state_schema: "./state.json" # 定义 draft -> submitted -> approved/rejected steps: - name: parse_application type: subagent subagent_name: "application-parser-v1.3" config: timeout_ms: 5000 input_mapping: raw_data: "$.input.raw_application_json" - name: check_identity type: subagent subagent_name: "id-verification-v2.1" config: confidence_threshold: 0.85 input_mapping: id_card_image: "$.parse_application.id_card_base64" output_mapping: id_number: "$.id_number" name: "$.name" - name: calculate_score type: subagent subagent_name: "credit-scoring-v3.0" input_mapping: id_number: "$.check_identity.id_number" income: "$.parse_application.income" debt_ratio: "$.parse_application.debt_ratio" output_mapping: score: "$.credit_score" risk_level: "$.risk_level" - name: make_decision type: subagent subagent_name: "decision-engine-v1.0" input_mapping: credit_score: "$.calculate_score.score" risk_level: "$.calculate_score.risk_level" application_id: "$.input.application_id" output_mapping: decision: "$.decision" next_state: "$.next_state" - name: notify_result type: subagent subagent_name: "notification-sender-v1.1" condition: "$.make_decision.decision == 'approved'" input_mapping: phone: "$.parse_application.phone" message: "您的贷款已获批,额度 ¥{{ $.calculate_score.score * 1000 }}"这个 workflow 的精妙之处在于condition字段。notify_result只在决策为approved时执行,否则跳过。更重要的是,Harness 会把这个 condition 编译成 WASM 字节码,在 runtime 高速执行,而不是用 JavaScript 解释器——实测 condition 判断耗时 < 0.1ms。
我们还加了一个隐藏技巧:在decision-engine-v1.0的 manifest 中,声明了side_effects: ["send_to_audit_log"]。这意味着每当这个 subagent 执行,Harness 会自动调用audit-loggersubagent,记录完整的决策依据(包括 score、risk_level、原始 input)。这个 audit log 不经过 workflow 定义,而是 Harness 的基础设施能力,确保了合规审计的不可篡改性。
5. 常见问题与排查技巧实录
5.1 “Workflow 卡在 pending 状态” —— 90% 是 Seam 总线问题
这是内网部署中最常见的问题。现象:workflow 启动后,harness-cli list workflows显示 status 一直是pending,日志里没有 error。
排查路径:
- 检查
seam-broker进程是否存活:ps aux | grep seam-broker - 查看
seam-broker日志:journalctl -u harness-seam-broker -n 100 - 最常见原因:Redis 连接超时。
seam-broker默认连接redis://localhost:6379,但内网服务器可能 Redis 在 6380 端口。解决方案:修改harness.yaml的seam.redis.url。 - 次常见原因:PostgreSQL 连接池耗尽。
seam-broker默认 10 个连接,当并发 workflow > 10 时,新请求会排队。解决方案:调大seam.postgres.max_connections。
独家技巧:用
harness-cli debug seam-topology命令查看当前 Seam 总线的拓扑图。它会显示所有已注册的 subagent 及其健康状态。如果某个 subagent 显示unhealthy,说明它的/health接口返回非 200,这时要单独 curl 它的 health 端点排查。
5.2 “Subagent 报 schema mismatch” —— 不是数据错,是契约未更新
现象:OCR subagent 输出{"id_number": "110101..."},但公安库 subagent 报错field id_number not found in input。
根本原因:公安库 subagent 的 manifest 还在用旧版 schema,它期望的字段名是id_no,而不是id_number。
解决步骤:
- 找到公安库 subagent 的 manifest 文件(通常在
/var/lib/harness/subagents/id-verification/manifest.json) - 修改
"id_no"为"id_number" - 重新签名 subagent 二进制:
harness-cli sign ... - 重启 subagent 进程
注意:不能只改 manifest!必须重新签名,因为 Harness 在加载 subagent 时,会校验 manifest 的 hash 是否与签名匹配。我们曾试过只改 manifest,结果 Harness 启动时报
signature verification failed。
5.3 “并发量上不去,CPU 100% 卡死” —— Rust runtime 配置陷阱
现象:当并发请求从 100 QPS 提升到 500 QPS 时,Harness 主进程 CPU 达到 100%,所有 workflow 延迟飙升。
真相:这不是性能瓶颈,而是 tokio runtime 配置错误。Harness 默认用tokio::runtime::Builder::new_multi_thread(),但没设置 worker 数量。在 8 核服务器上,它会创建 8 个 worker thread,但每个 subagent 的 blocking task(如 OCR 的 tesseract 调用)会阻塞整个 thread。
解决方案:在harness.yaml中显式配置:
runtime: blocking_threads: 32 # 为 blocking task 预留 32 个线程 max_threads: 16 # 总 worker thread 数然后重启 Harness。实测后,500 QPS 下 CPU 降至 65%,P99 延迟从 2.1s 降到 380ms。
5.4 “内网无法访问外部模型 API” —— 代理配置的隐藏开关
现象:在内网服务器上,subagent 调用https://api.openai.com/v1/chat/completions一直 timeout。
原因:Harness 默认禁用所有外网访问,即使你配置了系统代理,Harness 也会绕过它。
正确解法:在 subagent 的 manifest 中,显式声明需要的外部域名:
{ "external_dependencies": ["api.openai.com", "api.anthropic.com"] }然后在harness.yaml中配置代理:
network: proxy: http: "http://proxy.internal:3128" https: "http://proxy.internal:3128"Harness 会为这些声明的域名启用代理,其他域名仍保持禁止。这样既满足了业务需求,又守住了安全红线。
5.5 “Workflow 执行结果不一致” —— 时间戳与随机数的陷阱
现象:同一个 input,两次 workflow 执行,credit-scoringsubagent 输出的score不同。
根因:该 subagent 使用了rand::thread_rng()生成随机种子,而 Rust 的thread_rng在不同线程中产生不同序列。Harness 的 subagent 可能在不同 worker thread 中执行。
修复方案:在 subagent 中,用 workflow 的consistency_token作为随机种子:
use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; let mut rng = ChaCha8Rng::from_seed( sha256::digest(format!("{}-score-seed", input.consistency_token)).into() ); let score = rng.gen_range(500..900);这样,同一个 workflow 实例,无论在哪台机器、哪个线程执行,score 都完全一致。我们用这个方法解决了 100% 的“结果不一致”投诉。
我在实际部署中发现,Harness 的强大不在于它有多炫酷的功能,而在于它把那些工程师天天在会议上争论的“最佳实践”,变成了代码里的强制约束。比如“每个服务要有健康检查”,它不是建议,而是 subagent 注册时的必填字段;比如“状态变更要可审计”,它不是流程文档,而是 state.json 里的 mandatory rule。这种把工程哲学落地为代码契约的能力,才是它真正难以被复制的核心壁垒。