Files
AgentCoord/backend/server.py

434 lines
14 KiB
Python
Raw Normal View History

2025-12-07 17:18:10 +08:00
import nest_asyncio
nest_asyncio.apply()
import os, sys, functools
print = functools.partial(print, flush=True) # 全局 flush
sys.stdout.reconfigure(line_buffering=True) # 3.7+ 有效
import asyncio
2024-04-07 15:04:00 +08:00
from flask import Flask, request, jsonify
import json
from DataProcess import Add_Collaboration_Brief_FrontEnd
from AgentCoord.RehearsalEngine_V2.ExecutePlan import executePlan
from AgentCoord.PlanEngine.basePlan_Generator import generate_basePlan
from AgentCoord.PlanEngine.fill_stepTask import fill_stepTask
from AgentCoord.PlanEngine.fill_stepTask_TaskProcess import (
fill_stepTask_TaskProcess,
)
from AgentCoord.PlanEngine.branch_PlanOutline import branch_PlanOutline
from AgentCoord.PlanEngine.branch_TaskProcess import branch_TaskProcess
from AgentCoord.PlanEngine.AgentSelectModify import (
AgentSelectModify_init,
AgentSelectModify_addAspect,
)
import os
import yaml
import argparse
# initialize global variables
yaml_file = os.path.join(os.getcwd(), "config", "config.yaml")
2025-12-07 17:18:10 +08:00
yaml_data = {}
2024-04-07 15:04:00 +08:00
try:
with open(yaml_file, "r", encoding="utf-8") as file:
yaml_data = yaml.safe_load(file)
except Exception:
2025-12-07 17:18:10 +08:00
yaml_data = {}
2024-04-07 15:04:00 +08:00
USE_CACHE: bool = os.getenv("USE_CACHE")
if USE_CACHE is None:
USE_CACHE = yaml_data.get("USE_CACHE", False)
else:
USE_CACHE = USE_CACHE.lower() in ["true", "1", "yes"]
AgentBoard = None
AgentProfile_Dict = {}
Request_Cache: dict[str, str] = {}
app = Flask(__name__)
2025-12-07 17:18:10 +08:00
from jsonschema import validate, ValidationError
AGENT_SELECTION_SCHEMA = {
"type": "object",
"properties": {
"AgentSelectionPlan": {
"type": "array",
"items": {
"type": "string",
"minLength": 1, # 不允许空字符串
"pattern": r"^\S+$" # 不允许仅空白
},
"minItems": 1 # 至少选一个
}
},
"required": ["AgentSelectionPlan"],
"additionalProperties": False
}
BASE_PLAN_SCHEMA = {
"type": "object",
"properties": {
"Plan_Outline": {
"type": "array",
"items": {
"type": "object",
"properties": {
"StepName": {"type": "string"},
"TaskContent": {"type": "string"},
"InputObject_List":{"type": "array", "items": {"type": "string"}},
"OutputObject": {"type": "string"},
},
"required": ["StepName", "TaskContent", "InputObject_List", "OutputObject"],
"additionalProperties": False,
},
}
},
"required": ["Plan_Outline"],
"additionalProperties": False,
}
def safe_join(iterable, sep=""):
"""保证 join 前全是 strNone 变空串"""
return sep.join("" if x is None else str(x) for x in iterable)
def clean_agent_board(board):
"""把 AgentBoard 洗成只含 str 的字典列表"""
if not board:
return []
return [
{"Name": (a.get("Name") or "").strip(),
"Profile": (a.get("Profile") or "").strip()}
for a in board
if a and a.get("Name")
]
def clean_plan_outline(outline):
"""清洗 Plan_Outline 里的 None"""
if not isinstance(outline, list):
return []
for step in outline:
if not isinstance(step, dict):
continue
step["InputObject_List"] = [
str(i) for i in step.get("InputObject_List", []) if i is not None
]
step["OutputObject"] = str(step.get("OutputObject") or "")
step["StepName"] = str(step.get("StepName") or "")
step["TaskContent"] = str(step.get("TaskContent") or "")
return outline
2024-04-07 15:04:00 +08:00
@app.route("/fill_stepTask_TaskProcess", methods=["post"])
def Handle_fill_stepTask_TaskProcess():
incoming_data = request.get_json()
2025-12-07 17:18:10 +08:00
# print(f"[DEBUG] fill_stepTask_TaskProcess received data: {incoming_data}", flush=True)
# 验证必需参数
General_Goal = incoming_data.get("General Goal", "").strip()
stepTask_lackTaskProcess = incoming_data.get("stepTask_lackTaskProcess")
if not General_Goal:
return jsonify({"error": "General Goal is required and cannot be empty"}), 400
if not stepTask_lackTaskProcess:
return jsonify({"error": "stepTask_lackTaskProcess is required"}), 400
2024-04-07 15:04:00 +08:00
requestIdentifier = str(
(
"/fill_stepTask_TaskProcess",
2025-12-07 17:18:10 +08:00
General_Goal,
stepTask_lackTaskProcess,
2024-04-07 15:04:00 +08:00
)
)
if USE_CACHE:
if requestIdentifier in Request_Cache:
return jsonify(Request_Cache[requestIdentifier])
2025-12-07 17:18:10 +08:00
try:
filled_stepTask = fill_stepTask_TaskProcess(
General_Goal=General_Goal,
stepTask=stepTask_lackTaskProcess,
AgentProfile_Dict=AgentProfile_Dict,
)
except Exception as e:
print(f"[ERROR] fill_stepTask_TaskProcess failed: {e}", flush=True)
return jsonify({"error": str(e)}), 500
2024-04-07 15:04:00 +08:00
filled_stepTask = Add_Collaboration_Brief_FrontEnd(filled_stepTask)
Request_Cache[requestIdentifier] = filled_stepTask
response = jsonify(filled_stepTask)
return response
2025-12-07 17:18:10 +08:00
@app.route("/agentSelectModify_init", methods=["POST"])
2024-04-07 15:04:00 +08:00
def Handle_agentSelectModify_init():
2025-12-07 17:18:10 +08:00
incoming = request.get_json(silent=True) or {}
general_goal = (incoming.get("General Goal") or "").strip()
step_task = incoming.get("stepTask")
if not general_goal or not step_task:
return jsonify({"error": "Missing field"}), 400
if not AgentBoard: # 空 Board 直接返回
return jsonify({"AgentSelectionPlan": []})
req_id = str(("/agentSelectModify_init", general_goal, step_task))
if USE_CACHE and req_id in Request_Cache:
return jsonify(Request_Cache[req_id])
try:
clean_board = clean_agent_board(AgentBoard)
raw = AgentSelectModify_init(stepTask=step_task,
General_Goal=general_goal,
Agent_Board=clean_board)
if not isinstance(raw, dict):
raise ValueError("model returned non-dict")
plan = raw.get("AgentSelectionPlan") or []
cleaned = [str(x).strip() for x in plan if x is not None and str(x).strip()]
raw["AgentSelectionPlan"] = cleaned
validate(instance=raw, schema=AGENT_SELECTION_SCHEMA)
except Exception as exc:
print(f"[ERROR] AgentSelectModify_init: {exc}")
return jsonify({"error": str(exc)}), 500
2024-04-07 15:04:00 +08:00
if USE_CACHE:
2025-12-07 17:18:10 +08:00
Request_Cache[req_id] = raw
return jsonify(raw)
2024-04-07 15:04:00 +08:00
@app.route("/agentSelectModify_addAspect", methods=["post"])
def Handle_agentSelectModify_addAspect():
incoming_data = request.get_json()
requestIdentifier = str(
("/agentSelectModify_addAspect", incoming_data["aspectList"])
)
if USE_CACHE:
if requestIdentifier in Request_Cache:
return jsonify(Request_Cache[requestIdentifier])
scoreTable = AgentSelectModify_addAspect(
2025-12-07 17:18:10 +08:00
aspectList=incoming_data["aspectList"], Agent_Board=AgentBoard or []
2024-04-07 15:04:00 +08:00
)
Request_Cache[requestIdentifier] = scoreTable
response = jsonify(scoreTable)
return response
@app.route("/fill_stepTask", methods=["post"])
def Handle_fill_stepTask():
incoming_data = request.get_json()
2025-12-07 17:18:10 +08:00
# print(f"[DEBUG] fill_stepTask received data: {incoming_data}", flush=True)
# 验证必需参数
General_Goal = incoming_data.get("General Goal", "").strip()
stepTask = incoming_data.get("stepTask")
if not General_Goal:
return jsonify({"error": "General Goal is required and cannot be empty"}), 400
if not stepTask:
return jsonify({"error": "stepTask is required"}), 400
2024-04-07 15:04:00 +08:00
requestIdentifier = str(
(
"/fill_stepTask",
2025-12-07 17:18:10 +08:00
General_Goal,
stepTask,
2024-04-07 15:04:00 +08:00
)
)
if USE_CACHE:
if requestIdentifier in Request_Cache:
return jsonify(Request_Cache[requestIdentifier])
2025-12-07 17:18:10 +08:00
try:
filled_stepTask = fill_stepTask(
General_Goal=General_Goal,
stepTask=stepTask,
Agent_Board=AgentBoard,
AgentProfile_Dict=AgentProfile_Dict,
)
except Exception as e:
print(f"[ERROR] fill_stepTask failed: {e}", flush=True)
return jsonify({"error": str(e)}), 500
2024-04-07 15:04:00 +08:00
filled_stepTask = Add_Collaboration_Brief_FrontEnd(filled_stepTask)
Request_Cache[requestIdentifier] = filled_stepTask
response = jsonify(filled_stepTask)
return response
@app.route("/branch_PlanOutline", methods=["post"])
def Handle_branch_PlanOutline():
incoming_data = request.get_json()
requestIdentifier = str(
(
"/branch_PlanOutline",
incoming_data["branch_Number"],
incoming_data["Modification_Requirement"],
incoming_data["Existing_Steps"],
incoming_data["Baseline_Completion"],
incoming_data["Initial Input Object"],
incoming_data["General Goal"],
)
)
if USE_CACHE:
if requestIdentifier in Request_Cache:
return jsonify(Request_Cache[requestIdentifier])
branchList = branch_PlanOutline(
branch_Number=incoming_data["branch_Number"],
Modification_Requirement=incoming_data["Modification_Requirement"],
Existing_Steps=incoming_data["Existing_Steps"],
Baseline_Completion=incoming_data["Baseline_Completion"],
InitialObject_List=incoming_data["Initial Input Object"],
General_Goal=incoming_data["General Goal"],
)
branchList = Add_Collaboration_Brief_FrontEnd(branchList)
Request_Cache[requestIdentifier] = branchList
response = jsonify(branchList)
return response
@app.route("/branch_TaskProcess", methods=["post"])
def Handle_branch_TaskProcess():
incoming_data = request.get_json()
requestIdentifier = str(
(
"/branch_TaskProcess",
incoming_data["branch_Number"],
incoming_data["Modification_Requirement"],
incoming_data["Existing_Steps"],
incoming_data["Baseline_Completion"],
incoming_data["stepTaskExisting"],
incoming_data["General Goal"],
)
)
if USE_CACHE:
if requestIdentifier in Request_Cache:
return jsonify(Request_Cache[requestIdentifier])
branchList = branch_TaskProcess(
branch_Number=incoming_data["branch_Number"],
Modification_Requirement=incoming_data["Modification_Requirement"],
Existing_Steps=incoming_data["Existing_Steps"],
Baseline_Completion=incoming_data["Baseline_Completion"],
stepTaskExisting=incoming_data["stepTaskExisting"],
General_Goal=incoming_data["General Goal"],
AgentProfile_Dict=AgentProfile_Dict,
)
Request_Cache[requestIdentifier] = branchList
response = jsonify(branchList)
return response
2025-12-07 17:18:10 +08:00
@app.route("/generate_basePlan", methods=["POST"])
2024-04-07 15:04:00 +08:00
def Handle_generate_basePlan():
2025-12-07 17:18:10 +08:00
incoming = request.get_json(silent=True) or {}
general_goal = (incoming.get("General Goal") or "").strip()
initial_objs = incoming.get("Initial Input Object") or []
if not general_goal:
return jsonify({"error": "General Goal is required"}), 400
# 1. 空 Board 直接短路
if not AgentBoard:
print("[SKIP] AgentBoard empty")
out = Add_Collaboration_Brief_FrontEnd({"Plan_Outline": []})
return jsonify(out)
req_id = str(("/generate_basePlan", general_goal, initial_objs))
if USE_CACHE and req_id in Request_Cache:
return jsonify(Request_Cache[req_id])
try:
# 2. 洗 Board → 调模型 → 洗返回
clean_board = clean_agent_board(AgentBoard)
raw_plan = asyncio.run(
generate_basePlan(
General_Goal=general_goal,
Agent_Board=clean_board,
AgentProfile_Dict=AgentProfile_Dict,
InitialObject_List=initial_objs,
)
2024-04-07 15:04:00 +08:00
)
2025-12-07 17:18:10 +08:00
raw_plan["Plan_Outline"] = clean_plan_outline(raw_plan.get("Plan_Outline"))
validate(instance=raw_plan, schema=BASE_PLAN_SCHEMA) # 可选,二次校验
except Exception as exc:
print(f"[ERROR] generate_basePlan failed: {exc}")
return jsonify({"error": "model call failed", "detail": str(exc)}), 500
2024-04-07 15:04:00 +08:00
2025-12-07 17:18:10 +08:00
out = Add_Collaboration_Brief_FrontEnd(raw_plan)
2024-04-07 15:04:00 +08:00
if USE_CACHE:
2025-12-07 17:18:10 +08:00
Request_Cache[req_id] = out
return jsonify(out)
2024-04-07 15:04:00 +08:00
@app.route("/executePlan", methods=["post"])
def Handle_executePlan():
incoming_data = request.get_json()
requestIdentifier = str(
(
"/executePlan",
incoming_data["num_StepToRun"],
incoming_data["RehearsalLog"],
incoming_data["plan"],
)
)
if USE_CACHE:
if requestIdentifier in Request_Cache:
return jsonify(Request_Cache[requestIdentifier])
RehearsalLog = executePlan(
incoming_data["plan"],
incoming_data["num_StepToRun"],
incoming_data["RehearsalLog"],
AgentProfile_Dict,
)
Request_Cache[requestIdentifier] = RehearsalLog
response = jsonify(RehearsalLog)
return response
@app.route("/_saveRequestCashe", methods=["post"])
def Handle_saveRequestCashe():
with open(
os.path.join(os.getcwd(), "RequestCache", "Request_Cache.json"), "w"
) as json_file:
json.dump(Request_Cache, json_file, indent=4)
response = jsonify(
{"code": 200, "content": "request cashe sucessfully saved"}
)
return response
@app.route("/setAgents", methods=["POST"])
def set_agents():
global AgentBoard, AgentProfile_Dict
2025-12-07 17:18:10 +08:00
board_in = request.json or []
# 先清洗再赋值
AgentBoard = clean_agent_board(board_in)
AgentProfile_Dict = {a["Name"]: a["Profile"] for a in AgentBoard}
return jsonify({"code": 200, "content": "AgentBoard set successfully"})
2024-04-07 15:04:00 +08:00
def init():
global AgentBoard, AgentProfile_Dict, Request_Cache
with open(
os.path.join(os.getcwd(), "RequestCache", "Request_Cache.json"), "r"
) as json_file:
Request_Cache = json.load(json_file)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="start the backend for AgentCoord"
)
parser.add_argument(
"--port",
type=int,
default=8017,
help="set the port number, 8017 by defaul.",
)
args = parser.parse_args()
init()
app.run(host="0.0.0.0", port=args.port, debug=True)