agent1.3版本 新增agent创建以后自动注册功能 新增agent创建脚本

This commit is contained in:
handsomeAq 2026-08-04 17:15:10 +08:00
parent b8311451c8
commit 81acd16783
8 changed files with 526 additions and 0 deletions

View File

@ -45,8 +45,12 @@ from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactServ
from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
from google.adk.evaluation.in_memory_eval_sets_manager import InMemoryEvalSetsManager
from google.adk.evaluation.local_eval_set_results_manager import LocalEvalSetResultsManager
from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
from agents.luna.app import dev_app
# A2A 网关接入(注册 + 心跳),放最底部 import 以免循环依赖
import gateway_client
# 配置
HOST = os.getenv("API_SERVER_HOST", "0.0.0.0")
@ -82,6 +86,9 @@ def create_api_server() -> ApiServer:
# 认证服务(暂不需要,内存版占位)
credential_service = InMemoryCredentialService()
# 记忆服务(内存版占位:满足 ApiServer 必填参数,不含记忆工具/回调,不会注入记忆)
memory_service = InMemoryMemoryService()
# 评测集管理(暂不需要,占位)
eval_sets_manager = InMemoryEvalSetsManager()
eval_set_results_manager = LocalEvalSetResultsManager(agents_dir=DATA_DIR)
@ -89,6 +96,7 @@ def create_api_server() -> ApiServer:
return ApiServer(
agent_loader=DevAgentLoader(),
session_service=session_service,
memory_service=memory_service,
artifact_service=artifact_service,
credential_service=credential_service,
eval_sets_manager=eval_sets_manager,
@ -102,6 +110,13 @@ def main():
api_server = create_api_server()
fastapi_app = api_server.get_fast_api_app()
# 向 A2A 网关注册并启动心跳(注册失败不阻塞服务启动)
gateway_ok = gateway_client.register_agent(dev_app.name, f"http://127.0.0.1:{PORT}")
if gateway_ok:
gateway_client.start_heartbeat(dev_app.name)
else:
print("[gateway] 注册失败,跳过心跳(网关可能未启动或 auth 不对)")
print("=" * 60)
print("Luna Agent API Server 启动中...")
print(f" 模型: {dev_app.root_agent.model.model}")

View File

@ -0,0 +1,21 @@
C:\Users\nzy\AppData\Local\Programs\Python\Python314\Lib\site-packages\google\adk\features\_feature_decorator.py:72: UserWarning: [EXPERIMENTAL] feature FeatureName.PLUGGABLE_AUTH is enabled.
check_feature_enabled()
D:\nzy\workspace_python\agent\agents\my_agent\app.py:11: UserWarning: [EXPERIMENTAL] EventsCompactionConfig: This feature is experimental and may change or be removed in future versions without notice. It may introduce breaking changes at any time.
compaction_config = EventsCompactionConfig(
D:\nzy\workspace_python\agent\agents\my_agent\api_server.py:86: UserWarning: [EXPERIMENTAL] InMemoryCredentialService: This feature is experimental and may change or be removed in future versions without notice. It may introduce breaking changes at any time.
credential_service = InMemoryCredentialService()
C:\Users\nzy\AppData\Local\Programs\Python\Python314\Lib\site-packages\google\adk\auth\credential_service\in_memory_credential_service.py:33: UserWarning: [EXPERIMENTAL] BaseCredentialService: This feature is experimental and may change or be removed in future versions without notice. It may introduce breaking changes at any time.
super().__init__()
Traceback (most recent call last):
File "D:\nzy\workspace_python\agent\agents\my_agent\api_server.py", line 134, in <module>
main()
~~~~^^
File "D:\nzy\workspace_python\agent\agents\my_agent\api_server.py", line 105, in main
api_server = create_api_server()
File "D:\nzy\workspace_python\agent\agents\my_agent\api_server.py", line 92, in create_api_server
return ApiServer(
agent_loader=DevAgentLoader(),
...<6 lines>...
auto_create_session=True,
)
TypeError: ApiServer.__init__() missing 1 required keyword-only argument: 'memory_service'

View File

View File

@ -45,8 +45,12 @@ from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactServ
from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
from google.adk.evaluation.in_memory_eval_sets_manager import InMemoryEvalSetsManager
from google.adk.evaluation.local_eval_set_results_manager import LocalEvalSetResultsManager
from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
from agents.my_agent.app import dev_app
# A2A 网关接入(注册 + 心跳),放最底部 import 以免循环依赖
import gateway_client
# 配置
HOST = os.getenv("API_SERVER_HOST", "0.0.0.0")
@ -82,6 +86,9 @@ def create_api_server() -> ApiServer:
# 认证服务(暂不需要,内存版占位)
credential_service = InMemoryCredentialService()
# 记忆服务(内存版占位:满足 ApiServer 必填参数,不含记忆工具/回调,不会注入记忆)
memory_service = InMemoryMemoryService()
# 评测集管理(暂不需要,占位)
eval_sets_manager = InMemoryEvalSetsManager()
eval_set_results_manager = LocalEvalSetResultsManager(agents_dir=DATA_DIR)
@ -89,6 +96,7 @@ def create_api_server() -> ApiServer:
return ApiServer(
agent_loader=DevAgentLoader(),
session_service=session_service,
memory_service=memory_service,
artifact_service=artifact_service,
credential_service=credential_service,
eval_sets_manager=eval_sets_manager,
@ -102,6 +110,17 @@ def main():
api_server = create_api_server()
fastapi_app = api_server.get_fast_api_app()
# 挂载 A2A 网关任务接收端点POST /tasks/{request_id}
from agents.my_agent.task_receiver import router as task_router
fastapi_app.include_router(task_router)
# 向 A2A 网关注册并启动心跳(注册失败不阻塞服务启动)
gateway_ok = gateway_client.register_agent(dev_app.name, f"http://127.0.0.1:{PORT}")
if gateway_ok:
gateway_client.start_heartbeat(dev_app.name)
else:
print("[gateway] 注册失败,跳过心跳(网关可能未启动或 auth 不对)")
print("=" * 60)
print("Dev Agent API Server 启动中...")
print(f" 监听地址: http://{HOST}:{PORT}")

View File

@ -0,0 +1,120 @@
"""A2A 网关任务接收端点:接收网关主动推送的任务,后台执行 agent完成后回传结果。
契约网关 relay.py dispatch_command 推送:
POST {endpoint}/tasks/{request_id}
body: {"auth": GATEWAY_AUTH, "request_id": str, "payload": {...}}
成功响应 202立即确认执行完成后由后台线程回传网关 /api/agent/result
"""
import asyncio
import logging
import threading
from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import JSONResponse
import gateway_client
from agents.my_agent.app import dev_app
from google.adk.runners import InMemoryRunner
from google.genai import types as genai_types
logger = logging.getLogger(__name__)
router = APIRouter(tags=["tasks"])
# 与 api_server.py 一致的导入路径(独立运行时兜底)
import os
import sys
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
if PROJECT_ROOT not in sys.path:
sys.path.insert(0, PROJECT_ROOT)
def _payload_to_prompt(payload: dict) -> str:
"""将网关任务 payload 转换为 agent 的用户指令。"""
if not payload:
return "请执行任务并汇报结果。"
if "prompt" in payload and payload["prompt"]:
return str(payload["prompt"])
if "cmd" in payload and payload["cmd"]:
return f"请执行以下命令并汇报执行结果:\n{payload['cmd']}"
# 兜底:序列化整个 payload
return "请根据以下任务载荷执行并汇报结果:\n" + str(payload)
async def _run_agent_once(prompt: str, request_id: str) -> tuple[str, str]:
"""用 ADK InMemoryRunner 运行一次 agent返回 (接受任务后的首条回复, 最终总结)。
dev_app App 容器 agent 非裸 LlmAgentrun_async 不会自动创建
session需先用 runner.session_service 显式创建
"""
runner = InMemoryRunner(app=dev_app)
session_id = f"task-{request_id}"
await runner.session_service.create_session(
app_name=runner.app_name,
user_id="gateway",
session_id=session_id,
)
texts: list[str] = []
final_text = ""
async for event in runner.run_async(
user_id="gateway",
session_id=session_id,
new_message=genai_types.Content(role="user", parts=[genai_types.Part(text=prompt)]),
):
if event.is_final_response():
if event.content and event.content.parts:
for part in event.content.parts:
text = getattr(part, "text", None)
if text:
final_text += text
break
if event.content and event.content.parts:
for part in event.content.parts:
text = getattr(part, "text", None)
if text:
texts.append(text)
# 首条回复 = 接受任务后的第一条回应;最终总结 = final response
reply = (texts[0] if texts else final_text).strip()
summary = final_text.strip() or "\n".join(t for t in texts if t).strip() or "(无输出)"
return reply, summary
def _execute_and_report(request_id: str, payload: dict) -> None:
"""后台线程:执行 agent成功后回传 success异常回传 failed。"""
try:
prompt = _payload_to_prompt(payload)
reply, summary = asyncio.run(_run_agent_once(prompt, request_id))
gateway_client.report_result(
request_id,
agent_id=dev_app.name,
status="success",
progress=100,
result={"reply": reply, "output": summary},
)
except Exception as e:
logger.exception("agent task failed request=%s", request_id)
gateway_client.report_result(
request_id,
agent_id=dev_app.name,
status="failed",
progress=100,
error_info=str(e),
)
@router.post("/tasks/{request_id}")
async def receive_task(request_id: str, request: Request):
"""接收网关推送的任务,立即 202 确认,后台执行。"""
body = await request.json()
if body.get("auth") != gateway_client.GATEWAY_AUTH:
raise HTTPException(status_code=401, detail="invalid auth")
payload = body.get("payload") or {}
threading.Thread(
target=_execute_and_report,
args=(request_id, payload),
name=f"task-{request_id[:8]}",
daemon=True,
).start()
logger.info("task received request=%s payload=%s", request_id, payload)
return JSONResponse(status_code=202, content={"ok": True, "request_id": request_id, "status": "accepted"})

View File

@ -45,8 +45,12 @@ from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactServ
from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
from google.adk.evaluation.in_memory_eval_sets_manager import InMemoryEvalSetsManager
from google.adk.evaluation.local_eval_set_results_manager import LocalEvalSetResultsManager
from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
from agents.qwen.app import dev_app
# A2A 网关接入(注册 + 心跳),放最底部 import 以免循环依赖
import gateway_client
# 配置
HOST = os.getenv("API_SERVER_HOST", "0.0.0.0")
@ -82,6 +86,9 @@ def create_api_server() -> ApiServer:
# 认证服务(暂不需要,内存版占位)
credential_service = InMemoryCredentialService()
# 记忆服务(内存版占位:满足 ApiServer 必填参数,不含记忆工具/回调,不会注入记忆)
memory_service = InMemoryMemoryService()
# 评测集管理(暂不需要,占位)
eval_sets_manager = InMemoryEvalSetsManager()
eval_set_results_manager = LocalEvalSetResultsManager(agents_dir=DATA_DIR)
@ -89,6 +96,7 @@ def create_api_server() -> ApiServer:
return ApiServer(
agent_loader=DevAgentLoader(),
session_service=session_service,
memory_service=memory_service,
artifact_service=artifact_service,
credential_service=credential_service,
eval_sets_manager=eval_sets_manager,
@ -102,6 +110,13 @@ def main():
api_server = create_api_server()
fastapi_app = api_server.get_fast_api_app()
# 向 A2A 网关注册并启动心跳(注册失败不阻塞服务启动)
gateway_ok = gateway_client.register_agent(dev_app.name, f"http://127.0.0.1:{PORT}")
if gateway_ok:
gateway_client.start_heartbeat(dev_app.name)
else:
print("[gateway] 注册失败,跳过心跳(网关可能未启动或 auth 不对)")
print("=" * 60)
print("Qwen Agent API Server 启动中...")
print(f" 模型: {dev_app.root_agent.model.model}")

196
create_agent.py Normal file
View File

@ -0,0 +1,196 @@
"""
快速创建新 Agent 脚本
luna 模板复制出一个新 agent 目录自动替换所有唯一标识符
- 生成 agents/<dir>/ 下的 agent.py / app.py / api_server.py / chat.py / .env / __init__.py
- 生成 mcp_dev_agent/<dir>_server.py 入口
- 更新 C:/Users/nzy/.codebuddy/.mcp.json 追加 MCP server 条目
命名约定沿用 luna/qwen
DIR = 用户输入 claude
AGENT = {DIR}_agent App name / agent name
DB = sessions_{DIR}.db
MCP = kebab-case claude-agent
用法
python create_agent.py claude --model opcode/claude-sonnet
python create_agent.py claude --model opcode/claude-sonnet --port 8005 --display "Claude Dev Agent"
"""
import argparse
import json
import os
import re
import shutil
import sys
# 强制 UTF-8Windows 控制台默认 GBK无法打印 ✓/→ 等字符)
if sys.platform == "win32":
os.environ.setdefault("PYTHONUTF8", "1")
try:
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
except Exception:
pass
# 仓库根目录
REPO_ROOT = os.path.dirname(os.path.abspath(__file__))
AGENTS_DIR = os.path.join(REPO_ROOT, "agents")
MCP_DIR = os.path.join(REPO_ROOT, "mcp_dev_agent")
TEMPLATE_DIR = os.path.join(AGENTS_DIR, "luna")
MCP_CONFIG_PATH = os.path.join(os.path.expanduser("~"), ".codebuddy", ".mcp.json")
VENV_PYTHON = os.path.join(REPO_ROOT, ".venv", "Scripts", "python.exe")
# 模板中需要替换的标识符(按顺序执行,先长后短避免误伤)
REPLACEMENTS = [
("luna_agent", "{AGENT}"), # 覆盖所有 app/agent name 引用
("luna", "{DIR}"), # 剩余模块路径、db 名、instruction 内称呼
("Luna", "{TITLE}"), # 首字母大写Luna Agent / Luna: / === Luna
("8002", "{PORT}"), # 默认端口
("opcode/deepseek-v4-flash", "{MODEL}"), # .env 的 VLLM_MODEL
]
# 复制时排除的目录/文件
SKIP_DIR_NAMES = {"__pycache__", ".adk", ".git", ".idea"}
SKIP_FILENAMES = {".gitignore"}
def interact(arg_dir: str, model: str, port: int, display: str) -> tuple[str, str, int, str]:
"""补齐缺失参数(缺省时交互式询问)"""
d = arg_dir
if not d:
d = input("Agent 目录名(如 claude: ").strip()
if not model:
model = input("模型名(如 opcode/claude-sonnet: ").strip()
if port is None:
port = auto_next_port()
if not display:
display = f"{d.title()} Agent ({model}) 全栈开发助手"
return d, model, port, display
def auto_next_port() -> int:
"""扫描 agents/*/.env 的 API_SERVER_PORT取最大值 +1"""
max_port = 8000
if os.path.isdir(AGENTS_DIR):
for entry in os.listdir(AGENTS_DIR):
env_path = os.path.join(AGENTS_DIR, entry, ".env")
if os.path.isfile(env_path):
m = re.search(r"API_SERVER_PORT\s*=\s*(\d+)", open(env_path, encoding="utf-8").read())
if m:
max_port = max(max_port, int(m.group(1)))
return max_port + 1
def copy_tree(src: str, dst: str) -> None:
"""递归复制,跳过 __pycache__/.adk/.git 等"""
os.makedirs(dst, exist_ok=True)
for name in os.listdir(src):
s = os.path.join(src, name)
d = os.path.join(dst, name)
if os.path.isdir(s):
if name in SKIP_DIR_NAMES:
continue
copy_tree(s, d)
else:
if name in SKIP_FILENAMES:
continue
shutil.copy2(s, d)
def apply_replacements(path: str, mapping: dict) -> None:
"""对文件内容按顺序做字符串替换"""
with open(path, encoding="utf-8") as f:
content = f.read()
for old, key in REPLACEMENTS:
content = content.replace(old, mapping.get(key, ""))
with open(path, "w", encoding="utf-8") as f:
f.write(content)
def update_mcp_config(mcp_key: str, server_file: str, display: str) -> None:
"""在 .mcp.json 的 mcpServers 中追加一条"""
if not os.path.isfile(MCP_CONFIG_PATH):
print(f"[warn] 未找到 {MCP_CONFIG_PATH},跳过 MCP 配置更新")
return
with open(MCP_CONFIG_PATH, encoding="utf-8") as f:
cfg = json.load(f)
if mcp_key in cfg.get("mcpServers", {}):
print(f"[warn] .mcp.json 已存在 {mcp_key} 条目,跳过")
return
cfg.setdefault("mcpServers", {})[mcp_key] = {
"type": "stdio",
"command": VENV_PYTHON,
"args": [server_file],
"description": display,
}
with open(MCP_CONFIG_PATH, "w", encoding="utf-8") as f:
json.dump(cfg, f, ensure_ascii=False, indent=2)
print(f" ✓ 已更新 {MCP_CONFIG_PATH}")
def main() -> None:
parser = argparse.ArgumentParser(description="创建一个新的 Dev Agent从 luna 模板)")
parser.add_argument("dir", nargs="?", help="agent 目录名(如 claude")
parser.add_argument("--model", default=None, help="模型名(如 opcode/claude-sonnet")
parser.add_argument("--port", type=int, default=None, help="API 端口,默认自动取最大+1")
parser.add_argument("--display", default=None, help="MCP 描述,默认 '{Dir} Agent ({model}) 全栈开发助手'")
args = parser.parse_args()
d, model, port, display = interact(args.dir, args.model, args.port, args.display)
if not d or not model:
print("错误:目录名和模型名不能为空")
sys.exit(1)
agent = f"{d}_agent"
title = d.title()
db = f"sessions_{d}.db"
mcp_key = f"{d.replace('_', '-')}-agent"
target_dir = os.path.join(AGENTS_DIR, d)
server_file = os.path.join(MCP_DIR, f"{d}_server.py")
mapping = {"{AGENT}": agent, "{DIR}": d, "{TITLE}": title,
"{PORT}": str(port), "{MODEL}": model}
# 1. 校验目标目录不存在
if os.path.exists(target_dir):
print(f"错误:目录已存在 {target_dir}")
sys.exit(1)
if os.path.exists(server_file):
print(f"错误MCP server 已存在 {server_file}")
sys.exit(1)
print(f"创建 Agent: {d} (agent={agent}, port={port}, model={model})")
print(f" 数据库: {db}")
# 2. 复制模板目录
copy_tree(TEMPLATE_DIR, target_dir)
# 3. 替换所有文件里的标识符
for root, _dirs, files in os.walk(target_dir):
for fn in files:
apply_replacements(os.path.join(root, fn), mapping)
# 4. 重写 __init__.py
with open(os.path.join(target_dir, "__init__.py"), "w", encoding="utf-8") as f:
f.write(f"# {d} package\nfrom . import agent\n")
print(f" ✓ 已生成 agents/{d}/")
# 5. 生成 MCP server 入口
shutil.copy2(os.path.join(MCP_DIR, "luna_server.py"), server_file)
apply_replacements(server_file, mapping)
print(f" ✓ 已生成 {server_file}")
# 6. 更新 .mcp.json
update_mcp_config(mcp_key, server_file, display)
print("\n完成!启动方式:")
print(f" cd agents/{d} && python api_server.py # 启动 API Server自动注册到网关")
print(f" cd agents/{d} && python chat.py # 命令行对话")
print(f" python agent_status.py --agent {agent} # 查看状态(需先手动加入 AGENTS 映射)")
if __name__ == "__main__":
main()

140
gateway_client.py Normal file
View File

@ -0,0 +1,140 @@
"""A2A 网关接入客户端Agent 服务启动后自动注册 + 心跳保活 + 注销。
用法 api_server.py main() :
from gateway_client import register_agent, start_heartbeat, unregister_agent
if register_agent(dev_app.name, f"http://127.0.0.1:{PORT}"):
start_heartbeat(dev_app.name)
环境变量:
GATEWAY_URL 网关地址默认 http://127.0.0.1:8000
GATEWAY_AUTH 网关认证密码需与网关 GATEWAY_AUTH 一致
AGENT_TAGS 能力标签逗号分隔默认 code
AGENT_MAX_CONCURRENT 最大并发数默认 1
HEARTBEAT_INTERVAL 心跳间隔秒默认 10
"""
import logging
import os
import threading
import time
import httpx
logger = logging.getLogger(__name__)
GATEWAY_URL = os.getenv("GATEWAY_URL", "http://127.0.0.1:8000").rstrip("/")
GATEWAY_AUTH = os.getenv("GATEWAY_AUTH", "dev-gateway-auth")
HEARTBEAT_INTERVAL = int(os.getenv("HEARTBEAT_INTERVAL", "10"))
_client = httpx.Client(timeout=5.0)
def _parse_tags() -> list[str]:
raw = os.getenv("AGENT_TAGS", "code")
return [t.strip() for t in raw.split(",") if t.strip()]
def register_agent(agent_id: str, endpoint: str, agent_tags: list[str] | None = None,
max_concurrent: int | None = None) -> bool:
"""向网关注册本 Agent。成功返回 True失败网关未启动/认证失败)返回 False。"""
tags = agent_tags if agent_tags is not None else _parse_tags()
max_conc = max_concurrent or int(os.getenv("AGENT_MAX_CONCURRENT", "1"))
body = {
"auth": GATEWAY_AUTH,
"agent_id": agent_id,
"endpoint": endpoint,
"agent_tags": tags,
"max_concurrent": max_conc,
"current_load": 0,
}
try:
resp = _client.post(f"{GATEWAY_URL}/api/agent/register", json=body)
if resp.status_code == 200:
logger.info("registered to gateway agent=%s endpoint=%s tags=%s", agent_id, endpoint, tags)
return True
logger.error("register failed agent=%s status=%s body=%s", agent_id, resp.status_code, resp.text)
except httpx.HTTPError as e:
logger.error("register network error agent=%s err=%s", agent_id, e)
return False
def _heartbeat_once(agent_id: str, current_load: int) -> bool:
try:
resp = _client.post(
f"{GATEWAY_URL}/api/agent/heartbeat",
json={"agent_id": agent_id, "current_load": current_load},
)
if resp.status_code == 200:
return True
logger.warning("heartbeat failed status=%s body=%s", resp.status_code, resp.text)
except httpx.HTTPError as e:
logger.warning("heartbeat network error err=%s", e)
return False
def _heartbeat_loop(agent_id: str) -> None:
while True:
try:
load = get_current_load()
except Exception:
load = 0
_heartbeat_once(agent_id, load)
time.sleep(HEARTBEAT_INTERVAL)
def start_heartbeat(agent_id: str) -> threading.Thread:
"""启动后台心跳线程daemon随进程退出"""
t = threading.Thread(target=_heartbeat_loop, args=(agent_id,), name=f"gateway-hb-{agent_id}", daemon=True)
t.start()
logger.info("heartbeat started agent=%s interval=%ss", agent_id, HEARTBEAT_INTERVAL)
return t
def unregister_agent(agent_id: str) -> bool:
"""向网关注销本 Agent。"""
try:
resp = _client.post(f"{GATEWAY_URL}/api/agent/unregister", json={"agent_id": agent_id})
if resp.status_code in (200, 404):
logger.info("unregistered from gateway agent=%s", agent_id)
return True
logger.warning("unregister failed status=%s body=%s", resp.status_code, resp.text)
except httpx.HTTPError as e:
logger.error("unregister network error agent=%s err=%s", agent_id, e)
return False
def report_result(request_id: str, agent_id: str, status: str = "success",
progress: int = 100, result: dict | None = None,
error_info: str | None = None) -> bool:
"""任务执行完成后将结果回传给网关POST /api/agent/result
Args:
request_id: 网关分配的任务 ID
agent_id: Agent ID
status: success / failed
progress: 进度百分比
result: 结果字典可选
error_info: 错误信息失败时必填
"""
body = {
"request_id": request_id,
"agent_id": agent_id,
"status": status,
"progress": progress,
"result": result,
"error_info": error_info,
}
try:
resp = _client.post(f"{GATEWAY_URL}/api/agent/result", json=body)
if resp.status_code == 200:
logger.info("result reported request=%s status=%s", request_id, status)
return True
logger.error("report result failed request=%s status=%s body=%s", request_id, resp.status_code, resp.text)
except httpx.HTTPError as e:
logger.error("report result network error request=%s err=%s", request_id, e)
return False
def get_current_load() -> int:
"""Agent 当前负载。子类/调用方可覆写以报告真实并发数。"""
return 0