初始化

This commit is contained in:
gozeros committed 2026-09-07 18:24:55 +08:00
1 parent 34474bd5fc
commit b638b541d0
3008 files changed
+837699

No files matched your search

+483
View File
@@ -0,0 +1,483 @@
"""
测试流式接口的完整脚本
API流程:
1. 登录获取token
2. 创建thread(可选,也可以使用无状态接口)
3. 调用流式接口
两种方式:
- 方式1: POST /api/runs/stream (无状态,自动创建thread)
- 方式2: POST /api/threads/{thread_id}/runs/stream (需要先创建thread)
"""
import requests
import json
import asyncio
import httpx # 异步 HTTP 客户端
# ============ 配置区域 ============
BASE_URL = "http://127.0.0.1:8001"
LOGIN_URL = f"{BASE_URL}/api/v1/auth/login/username"
THREADS_URL = f"{BASE_URL}/api/threads"
RUNS_STREAM_URL = f"{BASE_URL}/api/runs/stream" # 无状态流式接口
# ============ 登录获取token ============
def get_token(username: str) -> str:
"""登录获取token"""
response = requests.post(
LOGIN_URL,
json={"username": username},
headers={"Content-Type": "application/json"}
)
# print(f"登录状态码: {response.status_code}")
data = response.json()
# print(f"登录响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
# 根据实际返回结构调整
token = data.get("token") or data.get("access_token") or data.get("data", {}).get("token")
return token
# ============ 方式2: 先创建thread,再调用流式接口 ============
def create_thread(token: str) -> str:
"""创建thread,返回thread_id"""
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
response = requests.post(
THREADS_URL,
json={}, # 空body,使用默认值
headers=headers
)
print(f"创建thread状态码: {response.status_code}")
data = response.json()
print(f"创建thread响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
return data.get("thread_id")
def get_agent_name_description(agent_name="Taiwan-intelligence-gathering-agent"):
url = f"{BASE_URL}/api/agents/{agent_name}"
token=get_token("ji")
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
response = requests.get(
url,
headers=headers,
)
print(response.json())
return response.json()["name"].lower(),"agent_name:"+response.json()["name"].lower()+","+response.json()["description"]
# ============ 创建智能体 ============
def create_agent(
name: str,
description: str = "",
model: str | None = None,
tool_groups: list[str] | None = None,
skills: list[str] | None = None,
soul: str = "",
):
"""
创建一个新的智能体
Args:
name: 智能体名称(必须是 hyphen-case,如 my-agent)
description: 智能体描述
model: 可选的模型覆盖(如 gpt-4, claude-3-opus 等)
tool_groups: 工具组白名单(如 ["web", "file"])
skills: 技能白名单(如 ["skill1", "skill2"])
soul: SOUL.md 内容(智能体人格/身份描述)
Returns:
创建结果
"""
TOKEN =get_token("ji")
url = f"{BASE_URL}/api/agents"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {TOKEN}"
}
payload = {
"name": name,
"description": description,
"soul": soul
}
# 添加可选字段
if model is not None:
payload["model"] = model
if tool_groups is not None:
payload["tool_groups"] = tool_groups
if skills is not None:
payload["skills"] = skills
print(f"请求: POST {url}")
print(f"请求体: {json.dumps(payload, indent=2, ensure_ascii=False)}")
response = requests.post(url, json=payload, headers=headers)
print(f"状态码: {response.status_code}")
if response.status_code == 201:
result = response.json()
print(f"✓ 智能体创建成功!")
print(f"响应: {json.dumps(result, indent=2, ensure_ascii=False)}")
return result
else:
print(f"✗ 创建失败: {response.text}")
return f"✗ 创建失败: {response.text}"
def create_main_agent(zhihui_name,agent_names):
d={}
token=get_token("ji")
l=[]
for i in agent_names:
# print(i)
name,description=get_agent_name_description(i)
thread_id_tmp=create_thread(token)
d[name]=thread_id_tmp
l.append(description)
name="你是总控控制协调智能体,负责协调其他智能体干活"
miaoshu=f"""#核心定位:
用途:跟用用户的任务目标,调用其他智能体干活,只负责协调汇总,当你觉得已有信息支持回答用户问题时候,停止调度,并输出最后的结果
#调度方法:
通过使用agent_orchestration技能调用其他智能体干活,使用技能时传入agent_name和需要做的活
#可调度智能体:
{l}"""
thread_id_tmp=create_thread(token)
engname=zhihui_name
d[engname]=thread_id_tmp
tmp_skills=["agent_orchestration"]
tmpres=create_agent(
name=engname,
description=name,
model="glm-5", # 指定模型
skills=tmp_skills, # 关联技能
soul=miaoshu)
# print("创建智能体响应:",tmpres)
if "失败" in tmpres and "exists" not in tmpres:
return tmpres
print(f"创建协调智能体成功,可以协调列表:{l}")
return d
import re
def extract_name_task(text="python3 scripts/agent_orchestration.py \"Us-intelligence-gathering-agent\" \"收集和分析美国人民对特朗普的看法,包括支持率和反对率、不同群体的观点差异、主要支持理由和反对理由等\""):
try:
pattern = r'\.py\s+"([^"]*)"\s+"([^"]*)"'
match = re.search(pattern, text)
if match:
agent_name = match.group(1)
task_content = match.group(2)
return agent_name,task_content
except:
return "",""
def update_agent_message(thread_id,new_content):
token=get_token("ji")
url = f"{BASE_URL}/api/threads/{thread_id}/state"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
tmp = requests.get(
url,
headers=headers
).json()
# print(tmp)
if "messages" in tmp["values"]:
tmp["values"]["messages"].append({"type": "human", "content": new_content})
else:
tmp["values"]["messages"]=[]
tmp["values"]["messages"].append({"type": "human", "content": new_content})
url = f"{BASE_URL}/api/threads/{thread_id}/state"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
response = requests.post(
url,
json=tmp,
headers=headers,
stream=True
)
del tmp
print(f"线程{thread_id}更新状态码: {response.status_code}")
def Broadcast_message(message,d_agent_thread_id,pingbi_agent_name):
for key in d_agent_thread_id:
if key not in pingbi_agent_name:
update_agent_message(d_agent_thread_id[key],message)
print("广播消息成功:",key,message)
null=None
true=True
false=False
async def leader_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names):
"""使用 httpx 异步请求的 leader_agent"""
# 使用 to_thread 异步运行同步的 Broadcast_message
await asyncio.to_thread(Broadcast_message, "总控智能体收到用户消息:" + new_message, d_agent_thread_id, [agent_name])
thread_id = d_agent_thread_id[agent_name]
l = []
token = get_token("ji")
url = f"{BASE_URL}/api/threads/{thread_id}/runs/stream"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
payload = {
"input": {
"messages": [
{
"type": "human",
"content": [
{
"type": "text",
"text": new_message
}
],
"additional_kwargs": {}
}
]
},
"config": {
"recursion_limit": 1000
},
"context": {
"agent_name": agent_name,
"model_name": "deepseek-chat",
"mode": "pro",
"reasoning_effort": "medium",
"thinking_enabled": true,
"is_plan_mode": false,
"subagent_enabled": false,
"thread_id": thread_id
},
"stream_mode": [
"messages-tuple",
"values",
],
"stream_subgraphs": true,
"stream_resumable": true,
"assistant_id": "lead_agent",
"on_disconnect": "continue",
"excluded_tools": ["web_search"],
"skill_stop_names": skill_stop_names,
}
async with httpx.AsyncClient(timeout=None) as client:
async with client.stream("POST", url, json=payload, headers=headers) as response:
print(f"状态码: {response.status_code}")
if response.status_code != 200:
body = await response.aread()
raise RuntimeError(
f"upstream {url} returned HTTP {response.status_code}: "
f"{body.decode('utf-8', 'replace')[:1000]}"
)
async for line in response.aiter_lines():
if line:
yield line
if line[:5] == "data:":
print(line)
l.append(line)
if len(l) < 2:
raise RuntimeError(
f"upstream stream produced no usable data lines (got {len(l)} line(s)). "
f"Check the server log on {url} for the real failure."
)
null = None
tmpres = eval(l[-2][5:])
tmpres = tmpres["messages"][-1]
tmp_task = []
if tmpres.get("tool_calls", []) == []:
yield {"status": []}
else:
for tool_call in tmpres.get("tool_calls", []):
try:
agentname, task_text = extract_name_task(str(tool_call["args"]))
print("agentname", agentname)
print("task_text:", task_text)
# 使用 to_thread 异步运行同步的 Broadcast_message
await asyncio.to_thread(Broadcast_message, f"总控智能体给{agentname}派活,内容为:" + task_text, d_agent_thread_id, [agent_name, agentname])
tmp_task.append([agentname, task_text])
except:
pass
yield {"status": tmp_task}
async def special_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names):
"""使用 httpx 异步请求的 special_agent"""
thread_id = d_agent_thread_id[agent_name]
l = []
token = get_token("ji")
url = f"{BASE_URL}/api/threads/{thread_id}/runs/stream"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
payload = {
"input": {
"messages": [
{
"type": "human",
"content": [
{
"type": "text",
"text": new_message
}
],
"additional_kwargs": {}
}
]
},
"config": {
"recursion_limit": 1000
},
"context": {
"agent_name": agent_name,
"model_name": "deepseek-chat",
"mode": "pro",
"reasoning_effort": "medium",
"thinking_enabled": true,
"is_plan_mode": false,
"subagent_enabled": false,
"thread_id": thread_id
},
"stream_mode": [
"messages-tuple",
"values",
],
"stream_subgraphs": true,
"stream_resumable": true,
"assistant_id": "lead_agent",
"on_disconnect": "continue",
"excluded_tools": ["web_search", "ask_clarification", "present_files", "view_image"],
"skill_stop_names": skill_stop_names,
}
async with httpx.AsyncClient(timeout=None) as client:
async with client.stream("POST", url, json=payload, headers=headers) as response:
print(f"状态码: {response.status_code}")
if response.status_code != 200:
body = await response.aread()
raise RuntimeError(
f"upstream {url} returned HTTP {response.status_code}: "
f"{body.decode('utf-8', 'replace')[:1000]}"
)
async for line in response.aiter_lines():
if line:
yield line
if line[:5] == "data:":
print(line)
l.append(line)
if len(l) < 2:
raise RuntimeError(
f"upstream stream produced no usable data lines (got {len(l)} line(s)). "
f"Check the server log on {url} for the real failure."
)
null = None
tmpres = eval(l[-2][5:])
tmpres = tmpres["messages"][-1]
# 使用 to_thread 异步运行同步的 Broadcast_message
await asyncio.to_thread(Broadcast_message, f"子智能体{agent_name}完成{new_message}工作,交付内容为:" + tmpres["content"], d_agent_thread_id, [agent_name])
yield {"status": f"子智能体{agent_name}完成{new_message}工作"}
async def agent_init(agent_names, main_agent_name):
"""异步的 agent_init,同步函数调用使用 to_thread"""
agent_names = [i.lower() for i in agent_names]
# 使用 to_thread 异步运行同步的 create_main_agent
tmpd = await asyncio.to_thread(create_main_agent, main_agent_name, agent_names)
message = """
你的职责:你只是若干个智能体的其中一员,你们的共同目标是完成一个庞大的任务,总控控制协调智能体会分配特定的活给你,你只需要用你自身的能力来完成这个特定的活
注意:你只是一个子智能体,你不必把你的分析结果存放到文件里面去,你只关心于完成总控控制协调智能体分配的活即可
"""
# 使用 to_thread 异步运行同步的 Broadcast_message
await asyncio.to_thread(Broadcast_message, message, tmpd, [main_agent_name])
return main_agent_name, tmpd
from fastapi import FastAPI, HTTPException,Body
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
import asyncio
import uvicorn
app = FastAPI()
# CORS 配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
import time
@app.post("/generate_mul_agent")
async def generate_stream(payload: dict = Body(...)):
"""
根据 payload 中的 agent_type 字段判断调用 leader_agent 还是 special_agent
payload 结构示例:
{
"agent_type": "leader", # 或 "special"
"d_agent_thread_id": {...},
"agent_name": "xxx",
"new_message": "xxx",
"skill_stop_names": [...]
}
"""
# 从 payload 中提取参数
agent_type = payload.get("agent_type", "leader") # 默认为 leader
d_agent_thread_id = payload.get("d_agent_thread_id", {})
agent_name = payload.get("agent_name", "")
new_message = payload.get("new_message", "")
skill_stop_names = payload.get("skill_stop_names", [])
# 流式响应生成器
async def stream_generator():
# 根据 agent_type 选择调用的函数
if agent_type == "leader":
async_generator = leader_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names)
else: # special
async_generator = special_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names)
# 异步迭代生成器
async for chunk in async_generator:
if isinstance(chunk, dict):
yield f"data: {json.dumps(chunk, ensure_ascii=False)}\n\n"
else:
yield f"{chunk}"
# 使用 SSE 流式响应
return StreamingResponse(
stream_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"X-Accel-Buffering": "no",
"Connection": "keep-alive",
}
)
@app.post("/agent_init")
async def init_agents(payload: dict = Body(...)):
"""
初始化智能体接口
payload 结构示例:
{
"agent_names": ["agent1", "agent2"],
"main_agent_name": "main-agent"
}
"""
agent_names = payload.get("agent_names", [])
main_agent_name = payload.get("main_agent_name", "main-agent")
# 调用异步的 agent_init
name, thread_id_dict = await agent_init(agent_names, main_agent_name)
return {
"status": "success",
"main_agent_name": name,
"thread_ids": thread_id_dict
}
if __name__ == "__main__":
uvicorn.run("testapi:app", host="0.0.0.0", port=8095, workers=1,limit_concurrency=1000) # 多进程提升并发能力
+483
View File
@@ -0,0 +1,483 @@
"""
测试流式接口的完整脚本
API流程:
1. 登录获取token
2. 创建thread(可选,也可以使用无状态接口)
3. 调用流式接口
两种方式:
- 方式1: POST /api/runs/stream (无状态,自动创建thread)
- 方式2: POST /api/threads/{thread_id}/runs/stream (需要先创建thread)
"""
import requests
import json
import asyncio
import httpx # 异步 HTTP 客户端
# ============ 配置区域 ============
BASE_URL = "http://127.0.0.1:8001"
LOGIN_URL = f"{BASE_URL}/api/v1/auth/login/username"
THREADS_URL = f"{BASE_URL}/api/threads"
RUNS_STREAM_URL = f"{BASE_URL}/api/runs/stream" # 无状态流式接口
# ============ 登录获取token ============
def get_token(username: str) -> str:
"""登录获取token"""
response = requests.post(
LOGIN_URL,
json={"username": username},
headers={"Content-Type": "application/json"}
)
# print(f"登录状态码: {response.status_code}")
data = response.json()
# print(f"登录响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
# 根据实际返回结构调整
token = data.get("token") or data.get("access_token") or data.get("data", {}).get("token")
return token
# ============ 方式2: 先创建thread,再调用流式接口 ============
def create_thread(token: str) -> str:
"""创建thread,返回thread_id"""
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
response = requests.post(
THREADS_URL,
json={}, # 空body,使用默认值
headers=headers
)
print(f"创建thread状态码: {response.status_code}")
data = response.json()
print(f"创建thread响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
return data.get("thread_id")
def get_agent_name_description(agent_name="Taiwan-intelligence-gathering-agent"):
url = f"{BASE_URL}/api/agents/{agent_name}"
token=get_token("ji")
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
response = requests.get(
url,
headers=headers,
)
print(response.json())
return response.json()["name"].lower(),"agent_name:"+response.json()["name"].lower()+","+response.json()["description"]
# ============ 创建智能体 ============
def create_agent(
name: str,
description: str = "",
model: str | None = None,
tool_groups: list[str] | None = None,
skills: list[str] | None = None,
soul: str = "",
):
"""
创建一个新的智能体
Args:
name: 智能体名称(必须是 hyphen-case,如 my-agent)
description: 智能体描述
model: 可选的模型覆盖(如 gpt-4, claude-3-opus 等)
tool_groups: 工具组白名单(如 ["web", "file"])
skills: 技能白名单(如 ["skill1", "skill2"])
soul: SOUL.md 内容(智能体人格/身份描述)
Returns:
创建结果
"""
TOKEN =get_token("ji")
url = f"{BASE_URL}/api/agents"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {TOKEN}"
}
payload = {
"name": name,
"description": description,
"soul": soul
}
# 添加可选字段
if model is not None:
payload["model"] = model
if tool_groups is not None:
payload["tool_groups"] = tool_groups
if skills is not None:
payload["skills"] = skills
print(f"请求: POST {url}")
print(f"请求体: {json.dumps(payload, indent=2, ensure_ascii=False)}")
response = requests.post(url, json=payload, headers=headers)
print(f"状态码: {response.status_code}")
if response.status_code == 201:
result = response.json()
print(f"✓ 智能体创建成功!")
print(f"响应: {json.dumps(result, indent=2, ensure_ascii=False)}")
return result
else:
print(f"✗ 创建失败: {response.text}")
return f"✗ 创建失败: {response.text}"
def create_main_agent(zhihui_name,agent_names):
d={}
token=get_token("ji")
l=[]
for i in agent_names:
# print(i)
name,description=get_agent_name_description(i)
thread_id_tmp=create_thread(token)
d[name]=thread_id_tmp
l.append(description)
name="你是总控控制协调智能体,负责协调其他智能体干活"
miaoshu=f"""#核心定位:
用途:跟用用户的任务目标,调用其他智能体干活,只负责协调汇总,当你觉得已有信息支持回答用户问题时候,停止调度,并输出最后的结果
#调度方法:
通过使用agent_orchestration技能调用其他智能体干活,使用技能时传入agent_name和需要做的活
#可调度智能体:
{l}"""
thread_id_tmp=create_thread(token)
engname=zhihui_name
d[engname]=thread_id_tmp
tmp_skills=["agent_orchestration"]
tmpres=create_agent(
name=engname,
description=name,
model="glm-5", # 指定模型
skills=tmp_skills, # 关联技能
soul=miaoshu)
# print("创建智能体响应:",tmpres)
if "失败" in tmpres and "exists" not in tmpres:
return tmpres
print(f"创建协调智能体成功,可以协调列表:{l}")
return d
import re
def extract_name_task(text="python3 scripts/agent_orchestration.py \"Us-intelligence-gathering-agent\" \"收集和分析美国人民对特朗普的看法,包括支持率和反对率、不同群体的观点差异、主要支持理由和反对理由等\""):
try:
pattern = r'\.py\s+"([^"]*)"\s+"([^"]*)"'
match = re.search(pattern, text)
if match:
agent_name = match.group(1)
task_content = match.group(2)
return agent_name,task_content
except:
return "",""
def update_agent_message(thread_id,new_content):
token=get_token("ji")
url = f"{BASE_URL}/api/threads/{thread_id}/state"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
tmp = requests.get(
url,
headers=headers
).json()
# print(tmp)
if "messages" in tmp["values"]:
tmp["values"]["messages"].append({"type": "human", "content": new_content})
else:
tmp["values"]["messages"]=[]
tmp["values"]["messages"].append({"type": "human", "content": new_content})
url = f"{BASE_URL}/api/threads/{thread_id}/state"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
response = requests.post(
url,
json=tmp,
headers=headers,
stream=True
)
del tmp
print(f"线程{thread_id}更新状态码: {response.status_code}")
def Broadcast_message(message,d_agent_thread_id,pingbi_agent_name):
for key in d_agent_thread_id:
if key not in pingbi_agent_name:
update_agent_message(d_agent_thread_id[key],message)
print("广播消息成功:",key,message)
null=None
true=True
false=False
async def leader_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names):
"""使用 httpx 异步请求的 leader_agent"""
# 使用 to_thread 异步运行同步的 Broadcast_message
await asyncio.to_thread(Broadcast_message, "总控智能体收到用户消息:" + new_message, d_agent_thread_id, [agent_name])
thread_id = d_agent_thread_id[agent_name]
l = []
token = get_token("ji")
url = f"{BASE_URL}/api/threads/{thread_id}/runs/stream"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
payload = {
"input": {
"messages": [
{
"type": "human",
"content": [
{
"type": "text",
"text": new_message
}
],
"additional_kwargs": {}
}
]
},
"config": {
"recursion_limit": 1000
},
"context": {
"agent_name": agent_name,
"model_name": "deepseek-chat",
"mode": "pro",
"reasoning_effort": "medium",
"thinking_enabled": true,
"is_plan_mode": false,
"subagent_enabled": false,
"thread_id": thread_id
},
"stream_mode": [
"messages-tuple",
"values",
],
"stream_subgraphs": true,
"stream_resumable": true,
"assistant_id": "lead_agent",
"on_disconnect": "continue",
"excluded_tools": ["web_search"],
"skill_stop_names": skill_stop_names,
}
async with httpx.AsyncClient(timeout=None) as client:
async with client.stream("POST", url, json=payload, headers=headers) as response:
print(f"状态码: {response.status_code}")
if response.status_code != 200:
body = await response.aread()
raise RuntimeError(
f"upstream {url} returned HTTP {response.status_code}: "
f"{body.decode('utf-8', 'replace')[:1000]}"
)
async for line in response.aiter_lines():
if line:
yield line
if line[:5] == "data:":
print(line)
l.append(line)
if len(l) < 2:
raise RuntimeError(
f"upstream stream produced no usable data lines (got {len(l)} line(s)). "
f"Check the server log on {url} for the real failure."
)
null = None
tmpres = eval(l[-2][5:])
tmpres = tmpres["messages"][-1]
tmp_task = []
if tmpres.get("tool_calls", []) == []:
yield {"status": []}
else:
for tool_call in tmpres.get("tool_calls", []):
try:
agentname, task_text = extract_name_task(str(tool_call["args"]))
print("agentname", agentname)
print("task_text:", task_text)
# 使用 to_thread 异步运行同步的 Broadcast_message
await asyncio.to_thread(Broadcast_message, f"总控智能体给{agentname}派活,内容为:" + task_text, d_agent_thread_id, [agent_name, agentname])
tmp_task.append([agentname, task_text])
except:
pass
yield {"status": tmp_task}
async def special_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names):
"""使用 httpx 异步请求的 special_agent"""
thread_id = d_agent_thread_id[agent_name]
l = []
token = get_token("ji")
url = f"{BASE_URL}/api/threads/{thread_id}/runs/stream"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
payload = {
"input": {
"messages": [
{
"type": "human",
"content": [
{
"type": "text",
"text": new_message
}
],
"additional_kwargs": {}
}
]
},
"config": {
"recursion_limit": 1000
},
"context": {
"agent_name": agent_name,
"model_name": "deepseek-chat",
"mode": "pro",
"reasoning_effort": "medium",
"thinking_enabled": true,
"is_plan_mode": false,
"subagent_enabled": false,
"thread_id": thread_id
},
"stream_mode": [
"messages-tuple",
"values",
],
"stream_subgraphs": true,
"stream_resumable": true,
"assistant_id": "lead_agent",
"on_disconnect": "continue",
"excluded_tools": ["web_search", "ask_clarification", "present_files", "view_image"],
"skill_stop_names": skill_stop_names,
}
async with httpx.AsyncClient(timeout=None) as client:
async with client.stream("POST", url, json=payload, headers=headers) as response:
print(f"状态码: {response.status_code}")
if response.status_code != 200:
body = await response.aread()
raise RuntimeError(
f"upstream {url} returned HTTP {response.status_code}: "
f"{body.decode('utf-8', 'replace')[:1000]}"
)
async for line in response.aiter_lines():
if line:
yield line
if line[:5] == "data:":
print(line)
l.append(line)
if len(l) < 2:
raise RuntimeError(
f"upstream stream produced no usable data lines (got {len(l)} line(s)). "
f"Check the server log on {url} for the real failure."
)
null = None
tmpres = eval(l[-2][5:])
tmpres = tmpres["messages"][-1]
# 使用 to_thread 异步运行同步的 Broadcast_message
await asyncio.to_thread(Broadcast_message, f"子智能体{agent_name}完成{new_message}工作,交付内容为:" + tmpres["content"], d_agent_thread_id, [agent_name])
yield {"status": f"子智能体{agent_name}完成{new_message}工作"}
async def agent_init(agent_names, main_agent_name):
"""异步的 agent_init,同步函数调用使用 to_thread"""
agent_names = [i.lower() for i in agent_names]
# 使用 to_thread 异步运行同步的 create_main_agent
tmpd = await asyncio.to_thread(create_main_agent, main_agent_name, agent_names)
message = """
你的职责:你只是若干个智能体的其中一员,你们的共同目标是完成一个庞大的任务,总控控制协调智能体会分配特定的活给你,你只需要用你自身的能力来完成这个特定的活
注意:你只是一个子智能体,你不必把你的分析结果存放到文件里面去,你只关心于完成总控控制协调智能体分配的活即可
"""
# 使用 to_thread 异步运行同步的 Broadcast_message
await asyncio.to_thread(Broadcast_message, message, tmpd, [main_agent_name])
return main_agent_name, tmpd
from fastapi import FastAPI, HTTPException,Body
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
import asyncio
import uvicorn
app = FastAPI()
# CORS 配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
import time
@app.post("/generate_mul_agent")
async def generate_stream(payload: dict = Body(...)):
"""
根据 payload 中的 agent_type 字段判断调用 leader_agent 还是 special_agent
payload 结构示例:
{
"agent_type": "leader", # 或 "special"
"d_agent_thread_id": {...},
"agent_name": "xxx",
"new_message": "xxx",
"skill_stop_names": [...]
}
"""
# 从 payload 中提取参数
agent_type = payload.get("agent_type", "leader") # 默认为 leader
d_agent_thread_id = payload.get("d_agent_thread_id", {})
agent_name = payload.get("agent_name", "")
new_message = payload.get("new_message", "")
skill_stop_names = payload.get("skill_stop_names", [])
# 流式响应生成器
async def stream_generator():
# 根据 agent_type 选择调用的函数
if agent_type == "leader":
async_generator = leader_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names)
else: # special
async_generator = special_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names)
# 异步迭代生成器
async for chunk in async_generator:
if isinstance(chunk, dict):
yield f"data: {json.dumps(chunk, ensure_ascii=False)}\n\n"
else:
yield f"{chunk}"
# 使用 SSE 流式响应
return StreamingResponse(
stream_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"X-Accel-Buffering": "no",
"Connection": "keep-alive",
}
)
@app.post("/agent_init")
async def init_agents(payload: dict = Body(...)):
"""
初始化智能体接口
payload 结构示例:
{
"agent_names": ["agent1", "agent2"],
"main_agent_name": "main-agent"
}
"""
agent_names = payload.get("agent_names", [])
main_agent_name = payload.get("main_agent_name", "main-agent")
# 调用异步的 agent_init
name, thread_id_dict = await agent_init(agent_names, main_agent_name)
return {
"status": "success",
"main_agent_name": name,
"thread_ids": thread_id_dict
}
if __name__ == "__main__":
uvicorn.run("testapi:app", host="0.0.0.0", port=8095, workers=1,limit_concurrency=1000) # 多进程提升并发能力
+132
View File
@@ -0,0 +1,132 @@
"""
OUT.py 的调用链 demo —— 跑通 agent_init → leader 派活 → 各 special agent 干活 这一整套流程。
前置条件:
1. DeerFlow 后端在 http://127.0.0.1:8001 在跑
2. OUT.py 已经 `python -m uvicorn OUT:app --host 0.0.0.0 --port 8095` 起好
3. 下面 SUB_AGENT_NAMES 里的 agent 已经在 DeerFlow 里创建好(/api/agents 能查到)
直接 `python demo_call.py` 即可。
"""
import asyncio
import json
import httpx
# ============ 改这里 ============
OUT_BASE = "http://127.0.0.1:8095"
# 已经在 DeerFlow 里建好的子 agent 名字 (会被 OUT.py 强制 lowercase)
SUB_AGENT_NAMES = [
"us-intelligence-gathering-agent",
"taiwan-intelligence-gathering-agent",
]
# 总控 agent 名字 —— OUT.py 会现场创建它,所以填一个新名字即可 (hyphen-case)
MAIN_AGENT_NAME = "demo-coordinator"
# 用户提问 —— 总控会基于此派活
USER_QUESTION = "对比中美两国 2024 年在 AI 监管政策上的差异"
# leader 阶段命中即停的 skill 名字单 (OUT.py 用 agent_orchestration 作为派活信号)
SKILL_STOP_NAMES = ["agent_orchestration"]
# ================================
async def call_agent_init() -> tuple[str, dict[str, str]]:
"""步骤 1: 初始化 —— 拿回 thread_ids 字典 {agent_name → thread_id}."""
print(">>> [1/3] POST /agent_init")
async with httpx.AsyncClient(timeout=300.0) as client:
r = await client.post(
f"{OUT_BASE}/agent_init",
json={
"agent_names": SUB_AGENT_NAMES,
"main_agent_name": MAIN_AGENT_NAME,
},
)
r.raise_for_status()
body = r.json()
main_name = body["main_agent_name"]
thread_ids = body["thread_ids"]
print(f" main_agent_name = {main_name}")
print(f" thread_ids = {json.dumps(thread_ids, ensure_ascii=False, indent=2)}")
return main_name, thread_ids
async def stream_generate(payload: dict) -> object | None:
"""通用 SSE 消费:边收边打印,最后返回 OUT.py 自己 yield 的 {'status': ...} 里的 status 字段."""
final_status = None
async with httpx.AsyncClient(timeout=None) as client:
async with client.stream("POST", f"{OUT_BASE}/generate_mul_agent", json=payload) as resp:
print(f" HTTP {resp.status_code}")
if resp.status_code != 200:
err = await resp.aread()
raise RuntimeError(f"OUT.py /generate_mul_agent failed: {err.decode('utf-8', 'replace')[:500]}")
async for line in resp.aiter_lines():
if not line:
continue
# OUT.py 把 DeerFlow 的原始 SSE 流直接转发,自己最后会再 yield 一行
# `data: {"status": ...}\n\n`。两边都以 `data:` 开头,我们都尝试 JSON parse
# 一下,能解析成 {"status": ...} 的就是 OUT.py 的终态。
if line.startswith("data:"):
body_text = line[5:].strip()
try:
obj = json.loads(body_text)
except json.JSONDecodeError:
continue
if isinstance(obj, dict) and "status" in obj:
final_status = obj["status"]
# 想看上游 DeerFlow 的完整流,把下面打开即可
# else:
# print(" raw:", line[:200])
return final_status
async def call_leader(main_agent_name: str, thread_ids: dict[str, str]) -> list[list[str]]:
"""步骤 2: leader 阶段 —— 总控读完用户问题,产出派活清单."""
print(f"\n>>> [2/3] POST /generate_mul_agent (leader, agent={main_agent_name})")
status = await stream_generate(
{
"agent_type": "leader",
"d_agent_thread_id": thread_ids,
"agent_name": main_agent_name,
"new_message": USER_QUESTION,
"skill_stop_names": SKILL_STOP_NAMES,
}
)
dispatch = status or []
print(f" 派活清单 = {json.dumps(dispatch, ensure_ascii=False, indent=2)}")
return dispatch
async def call_specials(thread_ids: dict[str, str], dispatch: list[list[str]]) -> None:
"""步骤 3: 把派活清单里每一项交给对应子 agent 跑."""
if not dispatch:
print("\n>>> [3/3] 总控没派出任何任务,直接结束。")
return
for idx, item in enumerate(dispatch, 1):
agent_name, task_text = item[0], item[1]
print(f"\n>>> [3/3] ({idx}/{len(dispatch)}) POST /generate_mul_agent (special, agent={agent_name})")
print(f" task = {task_text[:120]}{'...' if len(task_text) > 120 else ''}")
result = await stream_generate(
{
"agent_type": "special",
"d_agent_thread_id": thread_ids,
"agent_name": agent_name,
"new_message": task_text,
"skill_stop_names": [],
}
)
print(f" 完成: {result}")
async def main() -> None:
main_agent_name, thread_ids = await call_agent_init()
dispatch = await call_leader(main_agent_name, thread_ids)
await call_specials(thread_ids, dispatch)
print("\n✅ demo 跑完")
if __name__ == "__main__":
asyncio.run(main())