feat: implement hgctl agent module (#3267)

This commit is contained in:
xingpiaoliang
2025-12-26 13:47:32 +08:00
committed by GitHub
parent e7e3ab5ff6
commit 17e80b30fe
31 changed files with 5858 additions and 652 deletions

View File

@@ -0,0 +1,51 @@
from typing import Literal
from agentscope.agent import ReActAgent
from agentscope.formatter import FormatterBase
from agentscope.memory import LongTermMemoryBase, MemoryBase
from agentscope.model import ChatModelBase
from agentscope.plan import PlanNotebook
from agentscope.rag import KnowledgeBase
from agentscope.tool import Toolkit
from agentscope.tts import TTSModelBase
class Agent(ReActAgent):
def __init__(
self,
name: str,
sys_prompt: str,
model: ChatModelBase,
formatter: FormatterBase,
toolkit: Toolkit | None = None,
memory: MemoryBase | None = None,
long_term_memory: LongTermMemoryBase | None = None,
long_term_memory_mode: (
Literal["agent_control"] | Literal["static_control"] | Literal["both"]
) = "both",
enable_meta_tool: bool = False,
parallel_tool_calls: bool = False,
knowledge: KnowledgeBase | list[KnowledgeBase] | None = None,
enable_rewrite_query: bool = True,
plan_notebook: PlanNotebook | None = None,
print_hint_msg: bool = False,
max_iters: int = 10,
tts_model: TTSModelBase | None = None,
) -> None:
super().__init__(
name,
sys_prompt,
model,
formatter,
toolkit,
memory,
long_term_memory,
long_term_memory_mode,
enable_meta_tool,
parallel_tool_calls,
knowledge,
enable_rewrite_query,
plan_notebook,
print_hint_msg,
max_iters,
tts_model,
)

View File

@@ -0,0 +1,95 @@
import asyncio
from typing import Any
import os
import sys
from agentscope.agent import ReActAgent
from agentscope.memory import InMemoryMemory
from agentscope.message import Msg
from agentscope.pipeline._functional import stream_printing_messages
from agentscope.agent import ReActAgent
from agentscope.model import DashScopeChatModel
from agentscope.formatter import DashScopeChatFormatter
from agentrun.integration.agentscope import model, sandbox_toolset, toolset
from agentrun.sandbox import TemplateType
from agentrun.server import AgentRequest, AgentRunServer
from agentrun.utils.log import logger
from agent import Agent
from toolkit import toolkit, init_toolkit_sync
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "python"))
MODEL_NAME = "{{ .ChatModel }}"
SANDBOX_NAME = os.getenv("AGENTRUN_SANDBOX_NAME")
if not MODEL_NAME:
raise ValueError("请将 MODEL_NAME 替换为您已经创建的模型名称")
code_interpreter_tools = []
if SANDBOX_NAME and not SANDBOX_NAME.startswith("<"):
code_interpreter_tools = sandbox_toolset(
template_name=SANDBOX_NAME,
template_type=TemplateType.CODE_INTERPRETER,
sandbox_idle_timeout_seconds=300,
)
else:
logger.warning("SANDBOX_NAME 未设置或未替换,跳过加载沙箱工具。")
def load_sys_prompt(prompt_file_name="prompt.md"):
script_dir = os.path.dirname(os.path.abspath(__file__))
prompt_path = os.path.join(script_dir, prompt_file_name)
with open(prompt_path, 'r', encoding='utf-8') as f:
return f.read()
agent = Agent(
name="{{ .AgentName }}",
model=model(MODEL_NAME), # type: ignore
sys_prompt=load_sys_prompt(),
toolkit=toolkit,
memory=InMemoryMemory(),
formatter=DashScopeChatFormatter(),
)
async def invoke_agent(request: AgentRequest):
try:
content = request.messages[0].content
input_msg = Msg(
name="user_message",
content=content, # type: ignore
role="user",
)
async for msg, _ in stream_printing_messages(
agents=[agent],
coroutine_task=agent(input_msg),
):
text = msg.get_text_content()
if text:
yield text
except Exception:
logger.exception("调用出错")
raise
def main():
init_toolkit_sync()
AgentRunServer(invoke_agent=invoke_agent).start()
if __name__ == "__main__":
main()
"""
curl 127.0.0.1:9000/openai/v1/chat/completions -XPOST \
-H "content-type: application/json" \
-d '{
"messages": [{"role": "user", "content": "写一段代码,查询现在是几点?"}],
"stream":true
}'
"""

View File

@@ -0,0 +1,81 @@
edition: 3.0.0
name: agentrun-app
access: "{{ .AccessKey }}"
resources:
hgctl-agent2:
component: agentrun
props:
region: "{{ .Region }}"
# ============= 新规范agent 配置 =============
agent:
# 基本信息
name: "{{ .AgentName }}"
description: "{{ .AgentDesc }}"
# 代码配置(直接指定路径,支持目录或 zip 文件,或使用 OSS 代码包)
code:
src: .
# ossBucketName: funagent-agent-quickstart-langchain-demo-code
# ossObjectName: agentrun-quickstart-code.zip
language: python3.12
command:
- python3
- agentrun_main.py
# 容器配置(使用容器模式时配置此项)
# customContainerConfig:
# image: registry.cn-hangzhou.aliyuncs.com/my-app:latest
# command:
# - python3
# - app.py
# port: 9000
# 资源配置
cpu: 2.0
memory: 4096
diskSize: {{ .DiskSize }} # 可选,默认 512 MB
timeout: {{ .Timeout }} # 可选,默认 600 秒
# 端口和并发
port: {{ .Port }}
instanceConcurrency: 100
# 网络配置 - 仅公网访问
internetAccess: true
# VPC 配置(需要 VPC 内网访问时配置)
# vpcConfig:
# vpcId: vpc-xxx
# vSwitchIds: [vsw-xxx] # 支持单个或多个
# securityGroupId: sg-xxx
# internetAccess: true # 同时配置 vpcConfig 和 internetAccess 表示内外网都可访问
# 环境变量需要填写以下环境变量使用推荐使用无明文AK方式在下方填写授信给FC包含AliyunAgentRunFullAccess的执行角色
environmentVariables:
AGENTRUN_ACCESS_KEY_ID: "{{ .GlobalConfig.AlibabaCloudAccessKeyID }}"
AGENTRUN_ACCESS_KEY_SECRET: "{{ .GlobalConfig.AlibabaCloudAccessKeySecret }}"
AGENTRUN_ACCOUNT_ID: "{{ .GlobalConfig.AgentRunAccountID }}"
AGENTRUN_REGION: "{{ .GlobalConfig.AgentRunRegion }}"
# 执行角色填写此角色无需填写上方AK、SK敏感凭据的环境变量角色需要授信给FC包含AliyunAgentRunFullAccess
# role: acs:ram::1160216277279558:role/AliyunFCDefaultRole
# 日志配置
# logConfig:
# project: ws-testhz
# logstore: acs-ecs-system
# 端点配置
endpoints:
- name: prod
version: LATEST
description: "生产环境端点"
# 灰度发布示例
# - name: gray
# version: 2
# description: "灰度环境端点"
# weight: 0.2 # 20% 流量到版本 2

View File

@@ -0,0 +1,122 @@
import os
import asyncio
from agentscope_runtime.engine import AgentApp
from agentscope_runtime.engine.schemas.agent_schemas import AgentRequest
from agentscope.model import {{ .Provider }}Model
from agentscope.formatter import {{ .Provider }}Formatter
from agentscope_runtime.adapters.agentscope.memory import AgentScopeSessionHistoryMemory
from agentscope_runtime.engine.services.agent_state import InMemoryStateService
from agentscope_runtime.engine.services.session_history import InMemorySessionHistoryService
from agentscope.pipeline import stream_printing_messages
from agentscope_runtime.engine.deployers.local_deployer import LocalDeployManager
from agentscope_runtime.engine.deployers.utils.deployment_modes import DeploymentMode
from agent import Agent
from toolkit import toolkit, init_toolkit_sync
app = AgentApp(
app_name="{{.AppName}}",
app_description="{{.AppDescription}}",
)
def load_sys_prompt(prompt_file_name="prompt.md"):
script_dir = os.path.dirname(os.path.abspath(__file__))
prompt_path = os.path.join(script_dir, prompt_file_name)
with open(prompt_path, 'r', encoding='utf-8') as f:
return f.read()
@app.init
async def init_func(self):
"""初始化状态和会话服务"""
self.state_service = InMemoryStateService()
self.session_service = InMemorySessionHistoryService()
await self.state_service.start()
await self.session_service.start()
@app.shutdown
async def shutdown_func(self):
"""清理服务"""
await self.state_service.stop()
await self.session_service.stop()
@app.query(framework="agentscope")
async def query_func(self, msgs, request: AgentRequest, **kwargs):
session_id = request.session_id
user_id = request.user_id
# 恢复 Agent 状态
state = await self.state_service.export_state(
session_id=session_id,
user_id=user_id,
)
# ---- 创建 Agent ----
agent = Agent(
name="{{.AgentName}}",
model={{ .Provider }}Model(
"{{.ChatModel}}",
api_key=os.getenv("{{.APIKeyEnvVar}}"),
stream={{.EnableStreaming | boolToPython}},
),
sys_prompt=load_sys_prompt(),
toolkit=toolkit,
memory=AgentScopeSessionHistoryMemory(
service=self.session_service,
session_id=session_id,
user_id=user_id,
),
formatter={{ .Provider }}Formatter(),
)
agent.set_console_output_enabled(enabled=False)
# 恢复状态
if state:
agent.load_state_dict(state)
# ---- 流式输出 ----
async for msg, last in stream_printing_messages(
agents=[agent],
coroutine_task=agent(msgs),
):
yield msg, last
# ---- 保存 Agent 状态 ----
state = agent.state_dict()
await self.state_service.save_state(
user_id=user_id,
session_id=session_id,
state=state,
)
async def main():
"""以独立进程模式部署应用"""
deployment_info = await app.deploy(
LocalDeployManager(host="{{.HostBinding}}", port={{.DeploymentPort}}),
mode=DeploymentMode.DETACHED_PROCESS,
)
url = deployment_info['url']
print(f"✅ 部署成功:{url}")
print(f"📍 部署 ID{deployment_info['deploy_id']}")
print(
f"""
Check health: curl {url}/health
Shutdown: curl -X POST {url}/admin/shutdown
"""
)
print(f"🌟 You can deploy it to Higress by using: hgctl agent add {url}")
return deployment_info
if __name__ == "__main__":
init_toolkit_sync()
asyncio.run(main())

View File

@@ -0,0 +1,69 @@
import os
import asyncio
from agentscope.tool import Toolkit
from agentscope.tool import execute_shell_command
from agentscope.tool import view_text_file
from agentscope.tool import write_text_file
from agentscope.tool import insert_text_file
from agentscope.tool import dashscope_text_to_image
from agentscope.tool import dashscope_text_to_audio
from agentscope.tool import dashscope_image_to_text
from agentscope.tool import openai_text_to_image
from agentscope.tool import openai_text_to_audio
from agentscope.tool import openai_edit_image
from agentscope.tool import openai_create_image_variation
from agentscope.tool import openai_image_to_text
from agentscope.tool import openai_audio_to_text
from agentscope.tool import execute_python_code
from agentscope.mcp import HttpStatelessClient
toolkit = Toolkit()
def _register_tools():
{{range .AvailableTools}}
toolkit.register_tool_function({{.}})
{{else}}
pass
{{end}}
def init_toolkit_sync():
_register_tools()
asyncio.run(register_all_MCP(toolkit))
async def init_toolkit_async():
_register_tools()
await register_all_MCP(toolkit)
async def register_single_MCP(toolkit: Toolkit, mcp_config):
"""注册单个MCP服务器"""
headers = mcp_config.get("Headers") or None
api_client = HttpStatelessClient(
name=mcp_config["Name"],
transport=mcp_config["Transport"],
url=mcp_config["URL"],
headers=headers,
)
await toolkit.register_mcp_client(api_client)
async def register_all_MCP(toolkit: Toolkit):
"""注册所有配置的MCP服务器"""
{{- range .MCPServers }}
await register_single_MCP(toolkit, {
"Name": "{{ .Name }}",
"URL": "{{ .URL }}",
"Transport": "{{ .Transport }}",
"Headers": {
{{- range $key, $value := .Headers }}
"{{ $key }}": "{{ $value }}",
{{- end }}
}
})
{{- end }}