Skip to content

About

高性能 AI Agent Harness 后端,迭代中...

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

高性能 AI Agent Harness 后端

2026年10月21日,看到这个消息我是震惊的,DeepSeek将内部推理框架DeepInfer开源了,梁圣NB,致敬梁圣我的小项目也顺便开源了吧

用 nginx + Drogon + gRPC + MySQL/Redis 搭一套经典的高并发后端骨架,在里面承载一个 可插拔的 Agent 运行时(Agent Harness),并给长期记忆接上真实的 语义召回(RAG)。

  • src/harness/:Agent 运行时。按 OpenHarness 思路拆成七个模块 (engine / tools / skills / permissions / hooks / memory / mcp),全部通过构造函数注入, 可单独替换、单独测试。模型热插拔由 model_provider 作为基础设施支撑。
  • grpc-server:承载运行时的有状态后端(会话、长期记忆、审批待办都在这一侧)。
  • gateway:无状态 HTTP 网关,只做参数校验、协议转换、状态码映射,可水平扩容; nginx 在前做轮询负载均衡。

亮点

# 亮点 说明
1 语义长期记忆 写入时用 BAAI/bge-m3(1024 维)生成向量,召回时按查询向量做余弦 top-k;无 key 自动回退"最近 N 条"
2 七模块全部落地 并行工具真的并发、审批单真的落 Redis、上下文真的会压缩、MCP 真的能调外部工具
3 零 API key 可跑通全链路 内置 Fake LLM / Fake Embedding(规则驱动),make run-grpc-mock 即可复现全部分支
4 模型热插拔 接口 + 注册表;Fake 与 OpenAI 兼容实现按名注册,Agent 用 model 字段选型,业务零改动
5 回答可解释 响应带 status / confidence / model_used / activated_skills / context_tokens / context_compressed
6 人在环是同步确认 返回 NEEDS_HUMAN + pending_id;调用方带 approval_id + approval_action 重试即可原地恢复执行
7 上下文工程 token 估算 → 先历史压缩(LLM 生成摘要)→ 再裁剪,长会话不失控

1. 架构

                          ┌──────────────────────────────────────────┐
   客户端 / SDK           │           gateway (Drogon, N 实例)        │
        │                 │   AgentController → GrpcClient (stub)    │
        ▼                 └──────────────────┬───────────────────────┘
  ┌───────────┐  round-robin                │ gRPC
  │  nginx    │ ──────────────►──────────┐  │
  │  :8080    │   :8081 / :8082          │  │
  └───────────┘                          │  ▼
                                         │ ┌──────────────────────────────────────┐
                                         └►│      grpc-server (AgentServiceImpl)  │
                                           │              │                       │
                                           │              ▼                       │
                                           │        harness::Engine               │
                                           │   ├── ModelRegistry   LLM/Embedding  │
                                           │   ├── ToolRegistry    内置工具+并发  │
                                           │   ├── SkillManager    技能生命周期   │
                                           │   ├── PermissionManager 守卫+审批    │
                                           │   ├── HookRegistry    指标+审计      │
                                           │   ├── LongTermMemory  语义召回(RAG)  │
                                           │   └── McpClient       stdio JSON-RPC │
                                           │              │                       │
                                           │        AgentRepository               │
                                           └──────┬───────────────┬───────────────┘
                                                  ▼               ▼
                                           ┌─────────────┐ ┌─────────────┐
                                           │    MySQL    │ │    Redis    │
                                           │ 持久/连接池  │ │ 缓存 + hitl │
                                           └─────────────┘ └─────────────┘
                                                  │
                                    ┌─────────────┴─────────────┐
                                    ▼                           ▼
                            DeepSeek /chat/completions   SiliconFlow /embeddings
                            (LLM 生成)                  (BAAI/bge-m3 向量化)

分层职责

层 进程 职责 状态
接入层 nginx :8080 反向代理、轮询负载均衡 无状态
网关层 gateway :8081/:8082 REST 校验、gRPC 调用、状态码映射、X-Gateway-Instance 标记 无状态,可水平扩容
运行时层 grpc-server :9090 Agent Loop 编排、会话/记忆/待办、向量索引 有状态
存储层 MySQL / Redis 持久化 + Cache-Aside 缓存 + 待办 TTL —

2. 七模块

模块 组件 关键接口 实现要点
① 引擎 harness/engine Engine::Run Agent Loop、上下文工程、工具编排、记忆抽取
② 工具 harness/tools ToolRegistry::CallParallel 风险元数据 + std::async 批次并发(默认 4)
③ 技能 harness/skills SkillManager::Match 触发词 → 注入 prompt 片段 + 工具白名单;启停/版本/激活计数
④ 权限 harness/permissions CheckInput/CheckOutput/CheckTool 输入/输出守卫、风险分级、低置信度审批;审批单落 Redis 带 TTL
⑤ 钩子 harness/hooks HookRegistry::Run* OnBeforeChat/AfterChat/BeforeTool/AfterTool/OnError,旁路观察不阻断
⑥ 记忆 harness/memory Save / Recall / RecallSemantic 短期消息窗口 + 长期 MySQL 持久化 + 向量语义召回
⑦ MCP harness/mcp Start / CallTool fork/exec 子进程,stdio 上的 JSON-RPC 2.0;mutex 串行化往返

harness/model_provider(LlmModel / EmbeddingModel / ModelRegistry)是引擎依赖的 基础设施,不计入七模块。

一次 Run 的编排顺序

 0) 审批重试?        approval_id 非空 → Load 审批单 → approve 走 RunApproved / reject 直接熔断
 1) hooks.OnBeforeChat
 2) 输入守卫          PermissionManager::CheckInput(user_message)
 3) 技能匹配          SkillManager::Match(user_message) → 计入激活统计
 4) 长期记忆召回      LongTermMemory::RecallSemantic(agent_id, user_message, 5)
 5) 选择模型          ModelRegistry::GetLlm(req.model)          ← 热插拔点
 6) 确定工具集        内置(Agent 白名单 > 技能工具并集 > 全部)+ MCP 外部工具
 7) 短期记忆装载      history + 本轮 user
 8) 多轮循环 RunLoop(≤ max_turns,默认 4)
     ├─ 上下文工程 BuildContext():system 组装 → 摘要压缩 → token 裁剪
     ├─ 模型 CompleteWithRetry()(指数退避)
     ├─ 无工具调用 → 输出守卫 → 人工介入判定 → 抽取长期记忆 → OK
     └─ 有工具调用 → CheckTool(deny/高风险/低置信) → hooks → 执行
                    (内置并行 / MCP 串行)→ 回填 tool 消息 → 下一轮
 9) 收敛失败          超过 max_turns → status=ERROR(防死循环)
10) hooks.OnAfterChat

3. 语义长期记忆(RAG)

长期记忆不是"把最近几条塞回去",而是按语义相关性召回。

3.1 写入与召回

写入:user_message
        └─ Engine 调 Extract()(规则抽取 fact / preference)
             └─ LongTermMemory::Save()
                  ├─ EmbeddingModel::Embed(content)  → 1024 维 float
                  ├─ INSERT ... embedding=<hex 文本>, embedding_dim=1024
                  ├─ 增量更新内存索引 index_[id]
                  └─ DEL memory:<agent_id>(失效读缓存)

召回:user_message
        └─ LongTermMemory::RecallSemantic(agent_id, query, 5)
             ├─ EmbeddingModel::Embed(query) → 1024 维
             ├─ 在内存索引中筛出该 agent 的候选,算余弦相似度
             ├─ partial_sort 取 top-5
             ├─ 按 id 一次回表取原文(保持相似度顺序)
             └─ 注入 system prompt("已知的长期记忆")

3.2 为什么是"MySQL + 进程内索引"而不是向量数据库

选择 理由
MySQL 存向量 与业务数据同库同事务边界,便于备份/清理/排查;无需额外运维中间件
LONGTEXT 存 hex 纯 ASCII,拼 SQL 无需二进制转义;跨 MySQL/MariaDB/TiDB 行为一致;1024 维只占 8 KB
进程内余弦 单机记忆量级(万级)下暴力检索足够快;启动时从 MySQL 重建,写入时增量更新
显式降级 没有 key、embedding 失败、索引为空、回表失败都会回到"最近 N 条",记忆功能永不失效

索引结构:std::map<int64_t, IndexEntry{memory_key, embedding}> + std::mutex。 读路径只锁"遍历 + 打分",网络调用和 DB 回表都在锁外。

升级接缝:把 index_ 与 CosineSimilarity 换成 Milvus / pgvector 即可, Save / RecallSemantic 的对外语义不变。

3.3 表结构与兼容迁移

CREATE TABLE long_term_memories (
  id            BIGINT AUTO_INCREMENT PRIMARY KEY,
  memory_key    VARCHAR(128) NOT NULL,   -- 归属维度(当前用 agent_id)
  category      VARCHAR(32),             -- fact / preference
  content       TEXT,
  created_at    BIGINT NOT NULL,
  embedding     LONGTEXT NULL,           -- float32 按内存字节序转 hex(1024 维 = 8192 字符)
  embedding_dim INT NOT NULL DEFAULT 0,  -- 0 表示尚未向量化
  INDEX idx_memory_key (memory_key)
);

EnsureSchema() 先 CREATE TABLE IF NOT EXISTS,再用 SHOW COLUMNS ... LIKE 探测 embedding / embedding_dim 是否存在,缺列才 ALTER TABLE。 这样不依赖 ADD COLUMN IF NOT EXISTS(MySQL 8 不支持、MariaDB/TiDB 支持), 老库升级与新库初始化走同一条幂等路径。

3.4 五种降级路径

场景 行为
未配置 EMBEDDING_API_KEY vector_recall=off,RecallSemantic 直接调 Recall(最近 N 条)
查询 embedding 请求失败 记日志并回退 Recall,本轮对话不受影响
写入时 embedding 请求失败 原文照常入库(embedding=NULL),只记日志,不阻塞记忆沉淀
启动时发现历史记忆没有向量 LoadIndex() 逐条补向量并回写;失败的行下次启动再补
索引里没有该 agent 的候选 回退 Recall,避免"启用 RAG 后旧记忆彻底消失"

启动日志会明确当前状态:

[memory] 语义索引已加载 26 条(embedding=BAAI/bge-m3,本次回填 26 条)
[grpc-server] 长期记忆语义召回=on embedding=BAAI/bge-m3

无 key 时:

[grpc-server] 长期记忆语义召回=off(回退最近N条) embedding=fake-embedding

3.5 真实模型配置

# LLM:DeepSeek(OpenAI 兼容)
DEEPSEEK_API_KEY=sk-xxx DEEPSEEK_MODEL=deepseek-chat make run-grpc

# Embedding:SiliconFlow(OpenAI 兼容 /embeddings)
EMBEDDING_API_KEY=sk-xxx \
EMBEDDING_BASE_URL=https://api.siliconflow.cn/v1 \
EMBEDDING_MODEL=BAAI/bge-m3 \
EMBEDDING_DIM=1024 \
make run-grpc

配置了真实 embedding 时,它会被注册为默认 embedding,长期记忆直接取默认模型; 未配置时默认是 fake-embedding,但语义召回开关保持关闭。

4. 目录结构

http_server_demo/
├── proto/agent/v1/agent.proto     # 服务契约:Agent/Session/Chat/Tools/Skills/Memories/Stats
├── src/
│   ├── harness/                   # ★ Agent 运行时(七模块)
│   │   ├── types.h                # ChatMessage / ModelReply / ChatStatus / RiskLevel
│   │   ├── engine.*               # Agent Loop + 上下文工程(Run / RunApproved / RunLoop)
│   │   ├── model_provider.*       # 模型热插拔(Fake + OpenAI 兼容 + 注册表)
│   │   ├── tools.*                # 内置工具 + 风险元数据 + 并行执行
│   │   ├── skills.*               # 技能生命周期 + 触发词匹配
│   │   ├── permissions.*          # 输入/输出守卫 + 工具权限 + 审批单
│   │   ├── hooks.*                # 生命周期钩子(指标 + 审计日志)
│   │   ├── memory.*               # 短期记忆 + 长期记忆 + 向量索引(RAG)
│   │   └── mcp.*                  # MCP 客户端(fork/exec + stdio JSON-RPC)
│   ├── grpc_server/               # main.cpp(组件装配)+ agent_service(RPC 实现)
│   ├── gateway/                   # Drogon 控制器 + gRPC 客户端 + 单例上下文
│   ├── store/                     # MysqlPool / RedisClient / AgentRepository / HttpClient
│   └── common/                    # 命令行解析、JSON 工具
├── scripts/e2e_test.sh            # 端到端冒烟(14 个场景,Fake 模型即可)
├── scripts/stress_test.sh         # 压力测试(wrk/ab,分层场景 + 自动清理)
├── scripts/mcp_server.py          # 示例 MCP Server(stdio JSON-RPC,零依赖)
├── deploy/nginx/nginx.conf        # 轮询负载均衡配置
└── Makefile                       # proto / build / run-* / test / stress / lint / format

5. 快速开始

前置:MySQL(默认 127.0.0.1:3306,root 空密码,库 http_server_demo 自动创建)、 Redis(6379)、nginx。

make build            # 生成 proto 代码 + cmake 编译

# 终端 1:运行时后端(Fake LLM + Fake Embedding,零 API key)
make run-grpc-mock
# 终端 2/3:两个无状态网关实例
make run-gateway-1
make run-gateway-2
# 终端 4:nginx(:8080 → 轮询 :8081/:8082)
make run-nginx
# 终端 5:一键冒烟(14 个场景)
make test

体验一次:

curl -s -X POST http://127.0.0.1:8080/api/v1/agents \
  -H 'Content-Type: application/json' \
  -d '{"name":"助手","system_prompt":"你是一个简洁的中文助手。","tools":["get_time","calculator","get_weather","delete_record"]}'

curl -s -X POST http://127.0.0.1:8080/api/v1/sessions \
  -H 'Content-Type: application/json' -d '{"agent_id":"1"}'

curl -s -X POST http://127.0.0.1:8080/api/v1/chat \
  -H 'Content-Type: application/json' \
  -d '{"session_id":"1","message":"现在几点,顺便算一下 2+3,再看下上海天气"}'

接入真实模型与语义记忆:

DEEPSEEK_API_KEY=sk-xxx \
EMBEDDING_API_KEY=sk-xxx \
make run-grpc

创建 Agent 时用 {"model":"deepseek-chat"} 选择真实 LLM;embedding 不需要在 Agent 上指定, 长期记忆统一使用注册表里的默认 embedding。

6. REST API

所有响应都带 X-Gateway-Instance 头(观察负载均衡轮询)。

方法 路径 说明
POST /api/v1/agents 注册 Agent:{name, system_prompt?, model?, tools?} → 201
GET /api/v1/agents 列出 Agent
POST /api/v1/sessions 创建会话:{agent_id} → 201
GET /api/v1/sessions/{id} 查看会话(含历史消息)
POST /api/v1/chat ★ 发起一次对话(完整 Agent 运行时链路)
GET /api/v1/tools 工具列表(描述中带风险等级)
GET /api/v1/skills 技能列表(触发词 / 工具 / 启用状态 / 版本 / 激活次数)
POST /api/v1/skills/{name}/enable 启用或停用技能:{"enabled":true}
GET /api/v1/agents/{id}/memories 查看某 Agent 的长期记忆(时间倒序,管理面)
GET /api/v1/stats 统计(Agent/会话/请求/工具调用/记忆/技能/熔断)
GET /healthz 健康检查(无 /api/v1 前缀):{status, instance, grpc_addr}

POST /api/v1/chat

请求:

{
  "session_id": "1",
  "message": "现在几点,顺便算一下 2+3",
  "max_turns": 4,                  // 可选:覆盖运行时默认轮数
  "approval_id": "hitl-...",       // 可选:人工确认重试
  "approval_action": "approve"     // 可选:approve | reject
}

响应:

{
  "status": "OK",                  // OK | NEEDS_HUMAN | CIRCUIT_BREAK | ERROR
  "reply": "我已根据工具返回的结果整理完成。",
  "turns": 2,
  "confidence": 0.95,
  "model_used": "deepseek-chat",
  "pending_id": "",
  "reason": "",
  "activated_skills": ["time_helper", "calculator_helper"],
  "context_tokens": 86,
  "context_compressed": false,
  "tool_calls": [
    {"name": "get_time",   "arguments": "{}",                  "result": "{\"time\":\"2026-10-18 10:36:19\"}"},
    {"name": "calculator", "arguments": "{\"expression\":\"2+3\"}", "result": "{\"expression\":\"2+3\",\"result\":5}"}
  ]
}

HTTP 状态码约定

状态码 场景
200 对话结束(OK / CIRCUIT_BREAK)
201 创建 Agent / 会话成功
202 NEEDS_HUMAN —— 需人工确认后带 approval_id 重试
400 缺必填字段(name / agent_id / session_id / message)
404 Agent / 会话 / 技能不存在
502 gRPC 调用失败(后端不可用、超时、status=ERROR)

7. 关键机制

7.1 人在环:同步确认,可原地恢复

Chat → 判定需要确认(高风险工具 / 置信度不足)
     → 生成审批单 → Redis `SET hitl:<id> ... EX 300`
     → 返回 202 + status=NEEDS_HUMAN + pending_id

调用方带 {approval_id, approval_action:"approve"} 重试
     → 取回审批单 → RunApproved → 继续执行剩余工具 → 正常返回
reject → 不计入统计地拒绝 → status=CIRCUIT_BREAK

审批单里保存了 action_json(待执行动作),所以恢复时不需要重放前几轮对话。 原始 message 由调用方在重试时带回;服务端用 approval_id 判断这是重试, 不会重复落 user 消息(is_approval 分支)。

7.2 权限与熔断

判定点 规则
CheckInput 命中 banned_keywords(如 drop table / ignore previous)→ kDeny
CheckTool 工具在 denied_tools → kDeny;RiskLevel::kHigh → kAsk
CheckTool confidence < ask_confidence_threshold(默认 0.5)→ kAsk
CheckOutput 无回答且 confidence < min_confidence(默认 0.35)→ 熔断
CheckOutput finish_reason=content_filter → 熔断

熔断计数用 std::atomic<int64_t>,通过 GET /api/v1/stats 的 circuit_break_count 暴露。

7.3 上下文工程

system prompt(Agent 设定 / 全局默认 + 技能片段 + 长期记忆)
+ 历史消息(会话持久化)
+ 本轮 user
        │
        ├─ 估算 token → 超过 compress_threshold(1200) 且消息数 > keep_recent(6)
        │     → 用 LLM 生成摘要(summary 注入 system),替换较早消息
        └─ 仍超过 max_context_tokens(1800)
              → 裁剪:从最老开始丢,直到 ≤ 上限或只剩 keep_recent(6) 条

trimmed / compressed / context_tokens 会随响应返回,便于观察。

7.4 存储与缓存

层 内容 生命周期
MySQL agents Agent 配置(name/system_prompt/model/tools) 永久
MySQL sessions / messages / tool_calls 会话元数据、对话历史、工具调用记录 永久
MySQL long_term_memories 长期记忆 + 向量(hex) 永久
Redis memory:<agent_id> 长期记忆读缓存(JSON 数组) 30s TTL,写后失效
Redis hitl:<pending_id> 审批单 300s TTL

读写策略是 Cache-Aside:召回先查 Redis,未命中回源 MySQL 并回填; Save 成功后 DEL memory:<agent_id>。语义召回本身走内存索引,不依赖 Redis。

7.5 MCP 外部工具

McpClient 用 fork/exec 拉起 python3 scripts/mcp_server.py, 通过 stdin/stdout 上的 JSON-RPC 2.0(一行一条消息)完成:

  1. initialize 握手
  2. tools/list 发现工具(示例服务提供 3 个)
  3. tools/call 调用,取 content[].text 作为结果

stdio 是单通道,多个 gRPC 线程并发调用时用 mutex 串行化"写请求 → 读响应", 避免响应交错。MCP 启动失败只会降级为"仅内置工具",不影响服务启动。

8. 高并发与稳定性

  • 无状态网关:gateway 不保存任何会话状态,可随意加实例;nginx 轮询。
  • 连接池:MySQL 池默认 8 条,acquire() 返回 RAII guard,异常路径也能归还。
  • 并行工具:ToolRegistry::CallParallel 用 std::async 分批(默认 4), 结果顺序与入参严格一致。
  • 锁的粒度:注册表锁只保护"查找/复制",不保护工具调用本身; 长期记忆的索引锁只覆盖遍历打分,embedding 网络调用和回表都在锁外。
  • 防死循环:max_turns(默认 4)兜底,超轮直接 ERROR。
  • 失败可降级:MCP 失败 → 仅内置工具;embedding 失败 → 仅原文入库; Redis 不可用 → 记忆召回回源 MySQL(缓存层只是加速)。

8.1 压测方法与实测数据

压测入口是 scripts/stress_test.sh(make stress),链路为 wrk → nginx:8080 → gateway:8081/8082 → grpc_server:9090 → MySQL/Redis。 脚本优先用 wrk(Lua 构造 POST、自带 p50/p99 直方图),未安装时自动回退到 ab。

环境:Apple M2 Max(12 核)/ macOS 26.6.2 / wrk 4.2.0;grpc_server 与两个 gateway 同机运行,MySQL/Redis 本机,Fake LLM(无外部网络)。每组独立运行 10s(chat 5s), chat 场景预建 32 个会话轮转,避免单会话历史无限增长。

场景 并发 QPS p50 p99 错误
GET /healthz(仅网关) 10 55,956 0.14 ms 0.22 ms 0
50 60,537 0.79 ms 0.98 ms 0
100 60,742 1.63 ms 1.96 ms 0
200 59,350 3.36 ms 5.06 ms 0
GET /api/v1/agents(Redis 缓存池) 10 9,284 0.84 ms 1.45 ms 0
50 10,809 3.91 ms 14.10 ms 0
100 11,024 8.48 ms 30.64 ms 0
200 11,198 16.27 ms 60.30 ms 0
GET /api/v1/tools(进程内) 50 19,732 1.66 ms 97.24 ms 0
GET /api/v1/stats(Redis 计数) 10 9,783 0.80 ms 1.35 ms 0
50 10,891 3.76 ms 15.69 ms 0
100 10,868 8.59 ms 25.86 ms 0
200 10,810 17.59 ms 51.47 ms 0
POST /api/v1/chat(完整链路) 10 997 7.58 ms 113.62 ms 0
20 957 17.33 ms 189.01 ms 0
50 1,004 41.00 ms 251.81 ms 0

表中 agents/stats 是 Redis 连接池改造后(--redis-pool-size=8)的复测值; healthz/tools 不经过 Redis,沿用同机基线。chat 受后台负载影响较大, 多次复测在 0.96k–1.2k 之间波动,表中取同轮稳定值。

读法:

  • healthz 约 60k QPS 后走平,延迟随并发线性上升,说明入口(nginx/wrk 客户端) 已饱和;这一层不含存储访问,可作为链路基准。
  • agents/stats 从改造前的 ~8k 提升到 ~11k(约 +38%):RedisClient 已从 "单连接 + 全局 mutex"改为 8 条连接的池(--redis-pool-size,默认 8), 互斥锁只保护借/还两个 O(1) 动作,网络往返真正并行。对照实验(同一时刻、 同机、c=50):--redis-pool-size=1 时 agents=7.7k / stats=8.2k,复现旧上限; pool=8 时为 10.8k / 10.9k。
  • QPS 在 c=100 后仍走平(~11k):瓶颈已从 Redis 转移到网关/gRPC 转发层; tools(纯进程内、同样走网关)能到 ~20k,可作为该层上限的参考。
  • chat 稳态约 1.0k QPS:每个请求要读会话历史、写 user/assistant 消息、召回长期记忆、 更新统计,且共享一个 Engine 单例。pool=1 与 pool=8 的 chat 数据一致(约 0.96k), 说明 Redis 池化不是 chat 的瓶颈;p99 明显高于 p50,主要来自 MySQL 连接池(8) 排队与会话历史增长。真实模型模式下 LLM 网络时延会完全主导,这组数据只代表 "Fake 模型 + 本地存储" 的链路开销。

压测会写真实数据:chat 场景自动创建 stress-agent-* Agent/会话/消息, 结束后由脚本 trap EXIT 删除;异常退出可用 make stress-clean 兜底, KEEP_DATA=1 可保留现场。/api/v1/stats 的累计计数(requests/tool_calls) 会包含压测增量,不会回滚。

make stress                                  # healthz + agents + chat(c=50, 10s)
make stress-quick                            # healthz + agents(c=20, 5s)
make stress SCENARIOS=agents CONCURRENCY=100 DURATION=15s
make stress-clean                            # 清理上次压测遗留数据

9. 配置参数(grpc-server)

参数 默认值 说明
--port 9090 gRPC 监听端口
--mysql-host/port/user/password/database 127.0.0.1/3306/root/空/http_server_demo MySQL 连接池
--mysql-pool-size 8 连接池大小
--redis-host/port 127.0.0.1/6379 Redis
--redis-pool-size 8 Redis 连接池大小(每条连接同一时刻只归一个线程)
--llm-base-url DEEPSEEK_BASE_URL 或 https://api.deepseek.com OpenAI 兼容入口
--llm-api-key DEEPSEEK_API_KEY 为空则不注册真实 LLM
--llm-model deepseek-chat 注册名
--embedding-base-url EMBEDDING_BASE_URL 或 https://api.siliconflow.cn/v1 OpenAI 兼容 /embeddings
--embedding-api-key EMBEDDING_API_KEY 为空则语义召回关闭
--embedding-model BAAI/bge-m3 向量模型名
--embedding-dim 1024 向量维度
--max-turns 4 工具循环上限
--tool-concurrency 4 并行工具批次大小
--context-max-tokens 1800 上下文硬上限
--context-compress-tokens 1200 触发历史压缩的阈值
--context-keep-recent 6 裁剪时保留的最近消息数
--hitl-confidence 0.5 低于该置信度需人工确认
--hitl-ttl 300 审批单 TTL(秒)
--breaker-min-confidence 0.35 低于该置信度触发熔断
--mcp-script scripts/mcp_server.py MCP 服务脚本(传空串 = 禁用 MCP)
--mcp-cmd python3 MCP 子进程可执行程序
--mcp-timeout-ms 5000 单次 MCP 请求超时(毫秒)
--llm-timeout 60 LLM 请求超时(秒)
--llm-max-retries 1 LLM 失败重试次数
--llm-retry-backoff-ms 300 首次退避毫秒数(指数退避)
--system-prompt-file 空 全局默认 system prompt 文件路径

10. 常用命令

make proto        # 只重新生成 gRPC/protobuf 代码
make build        # proto + cmake 编译
make run-grpc     # 真实 LLM/Embedding(读环境变量)
make run-grpc-mock # Fake 模式,零外部依赖
make test         # 端到端冒烟(需要各进程已启动)
make stress       # 压力测试(wrk 优先、ab 兜底;chat 数据自动清理)
make stress-quick # 快速压测(healthz + agents,20 并发 / 5 秒)
make stress-clean # 清理上次压测遗留的 Agent/会话/消息
make lint         # clang-format 检查(Google 风格)
make format       # 自动格式化
make clean        # 清理构建产物

11. 已知限制

  • 向量索引在进程内存中,多实例部署时需要各自加载(或后续接 Milvus)。
  • 记忆抽取是规则驱动(Extract 匹配"我叫/我喜欢/记住"等),不是 LLM 抽取。
  • 人在环是同步阻塞语义:调用方必须带 pending_id 重试,暂不支持服务端回调。
  • MCP 的 stdio 通道用 mutex 串行化,外部工具调用无法并行。
  • Fake LLM 只用于打通链路,回答是固定模板,不代表真实模型质量。
  • 网关没有鉴权/租户隔离,默认假设部署在内网;生产需自行加认证与限流。

About

高性能 AI Agent Harness 后端,迭代中...

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages