接口修改
This commit is contained in:
1 parent
ab6b6876aa
commit
edb3238f3e
16 files changed
+2113
-54
No files matched your search
@@ -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) # 多进程提升并发能力
|
||||
Reference in new issue
Block a user