前面几篇一直在拆 Agent 的"脑袋":
- 它内部是一个循环:思考 → 行动 → 观察 → 记录;
- 它会用工具、会记忆、还能自我反思;
- 多个 Agent 可以组成一个"家族",通过调度器协作。
但在工程实践里,很多人做到这里会卡住:
这些 Agent 只能在一份 Python 脚本里跑,
其它项目想用,又要复制粘贴一大堆代码。
这篇就从工程落地的角度做一件事:
把一个 Agent 封装成一个简单的 HTTP 服务 ,
让它对外暴露一个统一接口:
POST /agent/infer→ 返回结构化结果。
整个过程只围绕技术实现,不讨论任何组织场景,方便你直接集成到自己的项目里。
一、目标长什么样?
先想清楚最终想要的使用方式。理想状态下,别的项目只需要:
curl -X POST http://localhost:8000/agent/infer \
-H "Content-Type: application/json" \
-d '{
"agent": "rag_qa",
"query": "请用3点概括这个系统的核心功能",
"context": {
"user_id": "u123",
"lang": "zh"
}
}'
然后拿到一个结构清晰的响应:
{
"agent": "rag_qa",
"ok": true,
"answer": "......",
"meta": {
"used_docs": [/* 文档id列表或片段 */],
"latency_ms": 853,
"trace_id": "20260208-abc123"
}
}
用一句话概括:
无论里面是多复杂的多智能体协作、RAG、工具调用,
对外就一个接口 + 一种统一返回格式。
二、抽象一个最小的 Agent 接口
先把内部实现统一成一个简单的 Python 接口,方便我们后面挂到 HTTP 层。
2.1 抽象基类
python
from abc import ABC, abstractmethod
from typing import Dict, Any
class BaseAgent(ABC):
def __init__(self, name: str):
self.name = name
@abstractmethod
def handle(self, query: str, context: Dict[str, Any]) -> Dict[str, Any]:
"""
:param query: 用户这次请求的自然语言或结构化指令
:param context: 额外上下文,比如 user_id / locale / 历史状态键等
:return: {
"answer": str,
"meta": {...} # 可选:调试信息、trace、引用内容等
}
"""
pass
之后你所有具体 Agent(RAG 问答、代码解释、报告生成等)都实现同一个 handle 签名,HTTP 层就可以做到**"不关心里面是谁,只要会 handle"**。
2.2 一个示例实现:RAG 问答 Agent
python
class RAGQAgent(BaseAgent):
def __init__(self, name, llm, retriever):
super().__init__(name)
self.llm = llm
self.retriever = retriever
def handle(self, query: str, context: Dict[str, Any]) -> Dict[str, Any]:
docs = self.retriever.retrieve(query, k=4)
ctx_text = "\n\n".join(docs)
prompt = (
"你是一个基于文档的问答助手,请严格参考给定内容回答问题。\n"
"如果文档中没有相关信息,请说明"资料中未提及"。\n\n"
f"【文档片段】\n{ctx_text}\n\n"
f"【问题】\n{query}\n"
)
answer = self.llm(prompt)
return {
"answer": answer,
"meta": {
"doc_count": len(docs),
"docs_preview": [d[:100] for d in docs]
}
}
此时,对 HTTP 层来说,它只看到一个:
python
agent = RAGQAgent("rag_qa", llm, retriever)
result = agent.handle(query, context)
三、构建一个简单的 Agent 注册表
为了避免在 HTTP 代码里写一堆 if agent_name == "xxx",建议搞一个注册表(Registry),按名字管理 Agent 实例。
python
class AgentRegistry:
def __init__(self):
self._agents = {}
def register(self, agent: BaseAgent):
self._agents[agent.name] = agent
def get(self, name: str) -> BaseAgent:
if name not in self._agents:
raise KeyError(f"未知 Agent: {name}")
return self._agents[name]
def list_agents(self):
return list(self._agents.keys())
在应用启动时集中注册:
python
registry = AgentRegistry()
# 假设已经初始化好了 llm、retriever 等
rag_qa_agent = RAGQAgent("rag_qa", llm, retriever)
summary_agent = SummaryAgent("summary", llm) # 自己实现
registry.register(rag_qa_agent)
registry.register(summary_agent)
这样,无论将来增加多少种 Agent,只需要在启动时 register 一下即可。
四、用一个轻量框架把 Agent 暴露成 HTTP 接口
这里用 FastAPI 举例(你也可以换成 Flask、Django 等),逻辑都类似:解析请求 → 找到对应 Agent → 调用 handle → 包装响应。
4.1 定义请求与响应模型
python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Dict, Any
app = FastAPI()
class AgentRequest(BaseModel):
agent: str
query: str
context: Dict[str, Any] = {}
class AgentResponse(BaseModel):
agent: str
ok: bool
answer: str
meta: Dict[str, Any] = {}
4.2 实现统一推理接口
python
import time
@app.post("/agent/infer", response_model=AgentResponse)
def infer(req: AgentRequest):
start = time.time()
try:
agent = registry.get(req.agent)
except KeyError:
raise HTTPException(status_code=404, detail=f"Agent '{req.agent}' 未注册")
try:
result = agent.handle(req.query, req.context or {})
latency_ms = (time.time() - start) * 1000
meta = result.get("meta", {})
meta["latency_ms"] = latency_ms
return AgentResponse(
agent=agent.name,
ok=True,
answer=result.get("answer", ""),
meta=meta
)
except Exception as e:
# 这里可以加日志
raise HTTPException(status_code=500, detail=str(e))
启动服务后,就得到了一个通用的 Agent 服务入口。
五、为 Agent 服务加上最小可用的"健康度"接口
为了方便排查问题,建议顺手加两个简单的辅助接口:
5.1 查看已注册的 Agent 列表
python
@app.get("/agent/list")
def list_agents():
return {"agents": registry.list_agents()}
5.2 健康检查
python
@app.get("/health")
def health():
return {"status": "ok"}
这样在部署后,用浏览器或命令行测试就很方便:
python
curl http://localhost:8000/agent/list
curl http://localhost:8000/health
六、在调用侧怎么接入这个 Agent 服务?
一旦把 Agent 封装成 HTTP 服务,其他项目就可以无需关心内部细节,通过 HTTP 客户端直接调用。
以 Python 为例:
python
import requests
def call_agent(server_url: str, agent: str, query: str, context=None):
payload = {
"agent": agent,
"query": query,
"context": context or {}
}
resp = requests.post(f"{server_url}/agent/infer", json=payload, timeout=30)
resp.raise_for_status()
data = resp.json()
if not data.get("ok"):
raise RuntimeError("Agent 调用失败")
return data["answer"], data.get("meta", {})
其它语言(Java、Go、JavaScript 等)也只需要写一个类似的 HTTP 客户端封装即可。
七、如何渐进式把你现有的多智能体系统"服务化"?
结合你前面已经实现的多智能体、自我反思、RAG 体系,可以按以下顺序慢慢迁移,不用"一次性大改":
-
先挑一个最核心的 Agent 做试点
比如
rag_qa或policy_explain这种最常用的问答 Agent:- 抽取成实现了
BaseAgent.handle的类; - 用上文的 FastAPI 逻辑包一层 HTTP。
- 抽取成实现了
-
逐步把其它 Agent 接进注册表
- ReportAgent、SummaryAgent、CodeAgent 等;
- 每个都保持统一签名:
handle(query, context)。
-
在旧系统中先改"调用方式",不改逻辑
- 先把原来直接调用 Python 类的地方,换成 HTTP 客户端调用;
- 确认行为一致后,再考虑进一步拆分、迁移。
-
最后再考虑拆分进不同进程、不同机器
- 一个服务里可以先挂多个 Agent;
- 请求量大了,再按 Agent 维度拆服务也比较自然。
做到这里,你的 Agent 体系就从:
只能在一个进程里本地调用的"类"
升级为:
任何语言、任何项目都可以通过 HTTP 复用的"服务"。
八、小结
这篇不再深挖 Agent 的内部算法,而是回到一个非常工程化的问题:
怎么让已经写好的 Agent,更容易被复用?
我做了几件事:
- 用一个统一的
BaseAgent.handle(query, context)抽象所有 Agent; - 用
AgentRegistry统一管理并按名字查找实例; - 用一个轻量 HTTP 框架包一层
/agent/infer的接口; - 为服务加上简单的
/agent/list与/health辅助接口; - 示范了调用方如何用 HTTP 客户端直接接 Agent。