gRPC 和服务部署
gRPC 服务器(grpc_app)
gRPC 应用将 GUI 智能体以长生命周期服务的形式对外暴露,用于多语言集成、远程编排、指标采集和持久化。 容器内的二进制入口:
/app/.venv/bin/lybic-guiagent-grpc(若在本地安装,且环境变量 PATH 已配置该工具所在的 bin 目录,可直接通过 lybic-guiagent-grpc 命令调用。)
Proto 文件
下表简要介绍了智能体 gRPC API 的主要接口及其用途,并给出典型调用示例:
| 方法 | 作用 | 示例 |
|---|---|---|
| GetAgentInfo | 获取智能体版本和能力信息 | resp = stub.GetAgentInfo(agent_pb2.GetAgentInfoRequest()) |
| GetGlobalCommonConfig | 获取全局通用配置 | resp = stub.GetGlobalCommonConfig(agent_pb2.GetGlobalCommonConfigRequest()) |
| SetGlobalCommonConfig | 设置全局通用配置 | resp = stub.SetGlobalCommonConfig(agent_pb2.SetGlobalCommonConfigRequest(commonConfig=...)) |
| SetGlobalCommonLLMConfig/SetGlobalGroundingLLMConfig/SetGlobalEmbeddingLLMConfig | 配置全局 LLM/嵌入/grounding 模型.参考文档Model Support & Configuration Reference | resp = stub.SetGlobalCommonLLMConfig(agent_pb2.SetGlobalCommonLLMConfigRequest(llmConfig=...)) |
| RunAgentInstruction | 同步执行自然语言指令,返回流式任务进度 | async for ev in stub.RunAgentInstruction(agent_pb2.RunAgentInstructionRequest(instruction="Open a calculator and compute 1+1")): |
| RunAgentInstructionAsync | 异步提交指令,返回 taskId | resp = await stub.RunAgentInstructionAsync(agent_pb2.RunAgentInstructionRequest(instruction="Create a new text document")) |
| GetAgentTaskStream | 获取异步任务流式进度 | async for ev in stub.GetAgentTaskStream(agent_pb2.GetAgentTaskStreamRequest(taskId=task_id)): |
| QueryTaskStatus | 查询任务状态和结果 | resp = await stub.QueryTaskStatus(agent_pb2.QueryTaskStatusRequest(taskId=task_id)) |
| CancelTask | 取消指定任务 | resp = await stub.CancelTask(agent_pb2.CancelTaskRequest(taskId=task_id)) |
详细参数请参考下方 proto 定义。
/*
1. generate protobuf code
python3 -m grpc_tools.protoc -Igui_agents/proto \
--python_out=gui_agents/proto/pb \
--grpc_python_out=gui_agents/proto/pb \
--pyi_out=gui_agents/proto/pb \
gui_agents/proto/agent.proto
2. resolve import path error:
sed -i 's/^import agent_pb2 as agent__pb2/from . import agent_pb2 as agent__pb2/' gui_agents/proto/pb/agent_pb2_grpc.py
*/
syntax = "proto3";
package lybic.agent;
option go_package = "github.com/lybic/lybic-sdk-go/agent";
option java_package = "com.lybic.agent";
option java_multiple_files = true;
option java_outer_classname = "AgentProto";
option py_generic_services = true;
import "google/protobuf/timestamp.proto";
service Agent {
// get agent info
rpc GetAgentInfo(GetAgentInfoRequest) returns (AgentInfo);
// get agent config
rpc GetGlobalCommonConfig(GetGlobalCommonConfigRequest) returns (CommonConfig);
rpc GetCommonConfig(GetCommonConfigRequest) returns (CommonConfig);
// set agent config
rpc SetGlobalCommonConfig(SetGlobalCommonConfigRequest) returns (SetCommonConfigResponse);
rpc SetGlobalCommonLLMConfig(SetGlobalCommonLLMConfigRequest) returns (LLMConfig);
rpc SetGlobalGroundingLLMConfig(SetGlobalGroundingLLMConfigRequest) returns (LLMConfig);
// set agent embedding
rpc SetGlobalEmbeddingLLMConfig(SetGlobalEmbeddingLLMConfigRequest) returns (LLMConfig);
// run agent sync
rpc RunAgentInstruction(RunAgentInstructionRequest) returns (stream TaskStream);
// run agent async
rpc RunAgentInstructionAsync(RunAgentInstructionRequest) returns (RunAgentInstructionAsyncResponse);
// get agent task stream
rpc GetAgentTaskStream(GetAgentTaskStreamRequest) returns (stream GetAgentTaskStreamResponse);
// query task status
rpc QueryTaskStatus(QueryTaskStatusRequest)returns (QueryTaskStatusResponse);
// cancel task
rpc CancelTask(CancelTaskRequest) returns (CancelTaskResponse);
}
message SetGlobalCommonLLMConfigRequest {
LLMConfig llmConfig = 1;
}
message SetGlobalEmbeddingLLMConfigRequest {
LLMConfig llmConfig = 1;
}
message SetGlobalGroundingLLMConfigRequest {
LLMConfig llmConfig = 1;
}
message SetGlobalCommonConfigRequest {
CommonConfig commonConfig = 1;
}
message SetCommonConfigResponse {
bool success = 1;
string id = 2;
}
message GetAgentInfoRequest {
}
message GetGlobalCommonConfigRequest {
}
message GetCommonConfigRequest {
string id = 1;
}
message AgentInfo {
string version = 1;
int32 maxConcurrentTasks = 2; // default 5
string log_level = 3;
optional string domain = 4;
}
message Sandbox {
// sandbox id
string id = 1;
// sandbox name
string name = 2;
// sandbox description
string description = 3;
// sandbox shape
string shapeName = 4;
// is hardware accelerated encoding
bool hardwareAcceleratedEncoding = 5;
// sandbox os type
SandboxOS os = 6;
// virtualization
string virtualization = 7; // KVM or Container
// architecture
string architecture = 8; // x86_64 or aarch64
}
enum SandboxOS {
OSUNDEFINED = 0;
WINDOWS = 1;
LINUX = 2;
ANDROID = 3;
}
message Authorization {
string orgID = 1;
string apiKey = 2;
optional string apiEndpoint = 3; // default https://api.lybic.cn
}
message LLMConfig {
string modelName = 1;
optional string provider = 2; // if not specified, agent will automatically find the provider form the model name (support doubao,gemini,deepseek)
optional string apiKey = 3;
optional string apiEndpoint = 4;
}
message StageModelConfig{
optional string webSearchEngine = 1;
optional LLMConfig contextFusionModel = 2;
optional LLMConfig subtaskPlannerModel = 3;
optional LLMConfig trajReflectorModel = 4;
optional LLMConfig memoryRetrivalModel = 5;
optional LLMConfig groundingModel = 6;
optional LLMConfig taskEvaluatorModel = 7;
optional LLMConfig actionGeneratorModel = 8;
optional LLMConfig actionGeneratorWithTakeoverModel = 9;
optional LLMConfig fastActionGeneratorModel = 10;
optional LLMConfig fastActionGeneratorWithTakeoverModel = 11;
optional LLMConfig dagTranslatorModel = 12;
optional LLMConfig embeddingModel = 13;
optional LLMConfig queryFormulatorModel = 14;
optional LLMConfig narrativeSummarizationModel = 15;
optional LLMConfig textSpanModel = 16;
optional LLMConfig episodeSummarizationModel = 17;
}
message CommonConfig {
string id = 1;
optional string backend = 2; // `lybic` for windows and linux or `lybic_mobile` for android
InstanceMode mode = 3; // agent mode default is FAST
optional int32 steps = 4; // default max steps is 50
string platform = 5; // agent target plat。 this is not used now
optional int32 taskTimeout = 6; // default 3600 (s)(1 hour)
optional Authorization authorizationInfo = 7; // lybic authorization info
optional StageModelConfig stageModelConfig = 8; // agent stage model config
}
enum InstanceMode {
MODEUNDEFINED = 0;
NORMAL = 1;
FAST = 2;
}
message RunAgentInstructionRequest {
// natural language instruction
string instruction = 1;
optional Sandbox sandbox = 2; // if sandbox is not specified, agent will create a sandbox to execute the instruction
optional CommonConfig runningConfig = 3; // if runningConfig is not specified, agent will use the default global config
optional bool destroySandbox = 4; // if true, destroy the sandbox after task completion (default: false)
optional string previousTaskId = 5; // if specified, restore conversation history from previous task to continue the context
}
message RunAgentInstructionAsyncResponse {
string taskId = 1;
}
enum TaskStatus {
TASKSTATUSUNDEFINED = 0;
PENDING = 1;
RUNNING = 2;
SUCCESS = 3;
FAILURE = 4;
NOT_FOUND = 5;
CANCELLED = 6;
}
message ExecutionStatistics {
int32 steps = 1;
int32 durationSeconds = 2;
int64 inputTokens = 3;
int64 outputTokens = 4;
int64 totalTokens = 5;
double cost = 6;
string currencySymbol = 7;
}
message QueryTaskStatusRequest {
string taskId = 1;
}
message QueryTaskStatusResponse {
string taskId = 1;
TaskStatus status = 2;
string message = 3;
string result = 4;
optional Sandbox sandbox = 5;
optional ExecutionStatistics executionStatistics = 6;
}
message GetAgentTaskStreamRequest {
string taskId = 1;
}
message GetAgentTaskStreamResponse {
TaskStream taskStream = 1;
}
message TaskStream{
string taskId = 1;
string stage = 2;
string message = 3;
google.protobuf.Timestamp timestamp = 4;
}
message CancelTaskRequest {
string taskId = 1;
}
message CancelTaskResponse {
string taskId = 1;
bool success = 2;
string message = 3;
}快速开始(本地)
LYBIC_API_KEY=your_key \
LYBIC_ORG_ID=your_org \
python -m gui_agents.grpc_app # 如果支持模块入口,否则使用已安装的脚本推荐:在 Docker 内运行以实现隔离。
Docker 运行
基础启动:
docker run --rm -it \
-p 50051:50051 \
--env-file gui_agents/.env \
agenticlybic/guiagent /app/.venv/bin/lybic-guiagent-grpc启用 Prometheus 指标:
docker run --rm -it \
-p 50051:50051 -p 8000:8000 \
-e ENABLE_PROMETHEUS=true -e PROMETHEUS_PORT=8000 \
--env-file gui_agents/.env \
agenticlybic/guiagent /app/.venv/bin/lybic-guiagent-grpc指标地址:http://HOST:8000/metrics。
核心环境变量
| 变量 | 用途 | 示例 |
|---|---|---|
LYBIC_API_KEY | 对 Lybic 沙箱 API 的认证 | sk_live_... |
LYBIC_ORG_ID | 指定所属组织 | org_123 |
LYBIC_PRECREATE_SID | 关联已预创建的沙箱 | SBX-XXXXXXXX |
LYBIC_MAX_LIFE_SECONDS | 沙箱生命周期(单位:秒) | 3600 |
ENABLE_PROMETHEUS | 暴露指标端点 | true |
PROMETHEUS_PORT | 指标端口 | 8000 |
TASK_STORAGE_BACKEND | 任务存储后端(memory 内存 / postgres 数据库) | postgres |
POSTGRES_CONNECTION_STRING | 数据库连接字符串 | postgresql://user:pwd@host:5432/db |
| 提供商密钥(Gemini/Ark 等) | 模型访问 | (参见 tools_config) |
可选持久化(PostgreSQL)
安装额外依赖:
pip install lybic-guiagents[postgres]
# 或在 Docker 镜像中已存在设置后端:
export TASK_STORAGE_BACKEND=postgres
export POSTGRES_CONNECTION_STRING=postgresql://user:password@db-host:5432/lybic_agent存储的数据:任务元数据、状态、历史记录,用于恢复 / 分析。
Docker Compose 示例
version: '3.9'
services:
agent-grpc:
image: agenticlybic/guiagent:latest
restart: unless-stopped
ports:
- '50051:50051'
- '8000:8000' # 指标
env_file: gui_agents/.env
environment:
ENABLE_PROMETHEUS: 'true'
PROMETHEUS_PORT: '8000'
TASK_STORAGE_BACKEND: 'postgres'
POSTGRES_CONNECTION_STRING: 'postgresql://agent:agent@postgres:5432/agent'
command: ['/app/.venv/bin/lybic-guiagent-grpc']
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: agent
POSTGRES_PASSWORD: agent
POSTGRES_DB: agent
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata: {}Kubernetes 部署片段
apiVersion: apps/v1
kind: Deployment
metadata:
name: lybic-agent-grpc
spec:
replicas: 2
selector:
matchLabels:
app: lybic-agent-grpc
template:
metadata:
labels:
app: lybic-agent-grpc
spec:
containers:
- name: agent
image: agenticlybic/guiagent:latest
ports:
- containerPort: 50051
- containerPort: 8000
env:
- name: LYBIC_API_KEY
valueFrom:
secretKeyRef:
name: lybic-secrets
key: api_key
- name: LYBIC_ORG_ID
valueFrom:
secretKeyRef:
name: lybic-secrets
key: org_id
- name: ENABLE_PROMETHEUS
value: 'true'
- name: PROMETHEUS_PORT
value: '8000'
- name: TASK_STORAGE_BACKEND
value: postgres
- name: POSTGRES_CONNECTION_STRING
valueFrom:
secretKeyRef:
name: lybic-secrets
key: pg_conn
readinessProbe:
tcpSocket:
port: 50051
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 50051
initialDelaySeconds: 15
periodSeconds: 20
---
apiVersion: v1
kind: Service
metadata:
name: lybic-agent-grpc
spec:
selector:
app: lybic-agent-grpc
ports:
- port: 50051
targetPort: 50051
- port: 8000
targetPort: 8000为 :8000/metrics 添加 Prometheus 抓取作业。
客户端使用(Python 异步)
流式传输(RunAgentInstruction)
返回实时 TaskStream 事件直到完成。
import asyncio, grpc
from gui_agents.proto.pb import agent_pb2, agent_pb2_grpc
async def streaming_example():
async with grpc.aio.insecure_channel("localhost:50051") as channel:
stub = agent_pb2_grpc.AgentStub(channel)
# (可选)配置定位/动作模型
await stub.SetGlobalGroundingLLMConfig(agent_pb2.SetGlobalGroundingLLMConfigRequest(
llmConfig=agent_pb2.LLMConfig(modelName="doubao-1-5-ui-tars-250428")
))
await stub.SetGlobalCommonLLMConfig(agent_pb2.SetGlobalCommonLLMConfigRequest(
llmConfig=agent_pb2.LLMConfig(modelName="claude-sonnet-4-20250514")
))
async for ev in stub.RunAgentInstruction(agent_pb2.RunAgentInstructionRequest(
instruction="Open a calculator and compute 1 + 1"
)):
print(f"[STREAM {ev.taskStream.stage}] {ev.taskStream.message}")
asyncio.run(streaming_example())异步生命周期(RunAgentInstructionAsync + GetAgentTaskStream + QueryTaskStatus)
import asyncio, grpc
from gui_agents.proto.pb import agent_pb2, agent_pb2_grpc
async def lifecycle_example():
async with grpc.aio.insecure_channel("localhost:50051") as channel:
stub = agent_pb2_grpc.AgentStub(channel)
# 启动异步任务
start = await stub.RunAgentInstructionAsync(agent_pb2.RunAgentInstructionRequest(
instruction="Create a new text document and write today's date"
))
task_id = start.taskId
print("Started task", task_id)
# 并发流和轮询
async def stream_events():
async for ev in stub.GetAgentTaskStream(agent_pb2.GetAgentTaskStreamRequest(taskId=task_id)):
print(f"[STREAM {ev.taskStream.stage}] {ev.taskStream.message}")
async def poll_status():
while True:
status = await stub.QueryTaskStatus(agent_pb2.QueryTaskStatusRequest(taskId=task_id))
print("[STATUS]", status.status.name)
if status.status in (agent_pb2.SUCCESS, agent_pb2.FAILURE, agent_pb2.CANCELLED):
print("Final result:", status.result)
if status.executionStatistics:
print("Steps:", status.executionStatistics.steps,
"Duration(s):", status.executionStatistics.durationSeconds,
"Tokens:", status.executionStatistics.totalTokens)
break
await asyncio.sleep(4)
await asyncio.gather(stream_events(), poll_status())
asyncio.run(lifecycle_example())取消任务
cancel_resp = await stub.CancelTask(agent_pb2.CancelTaskRequest(taskId=task_id))
print("Cancel success?", cancel_resp.success, cancel_resp.message)配置更新示例
# 仅更新定位模型
await stub.SetGlobalGroundingLLMConfig(agent_pb2.SetGlobalGroundingLLMConfigRequest(
llmConfig=agent_pb2.LLMConfig(modelName="doubao-1-5-ui-tars-250428")
))
# 更新动作生成器
await stub.SetGlobalCommonLLMConfig(agent_pb2.SetGlobalCommonLLMConfigRequest(
llmConfig=agent_pb2.LLMConfig(modelName="claude-sonnet-4-20250514")
))
# 设置完整全局配置(模式 FAST,最大 40 步)
await stub.SetGlobalCommonConfig(agent_pb2.SetGlobalCommonConfigRequest(
commonConfig=agent_pb2.CommonConfig(
id="global",
backend="lybic",
mode=agent_pb2.FAST,
steps=40,
authorizationInfo=agent_pb2.Authorization(orgID="org_123", apiKey="sk_live"),
stageModelConfig=agent_pb2.StageModelConfig(
groundingModel=agent_pb2.LLMConfig(modelName="doubao-1-5-ui-tars-250428"),
actionGeneratorModel=agent_pb2.LLMConfig(modelName="claude-sonnet-4-20250514")
)
)
))查询任务状态(仅轮询)
status = await stub.QueryTaskStatus(agent_pb2.QueryTaskStatusRequest(taskId=task_id))
print(status.status.name, status.result)
if status.executionStatistics:
print("Cost", status.executionStatistics.cost, status.executionStatistics.currencySymbol)最小One-Shot Helper
async def run_one_shot(instruction: str):
async with grpc.aio.insecure_channel("localhost:50051") as channel:
stub = agent_pb2_grpc.AgentStub(channel)
async for ev in stub.RunAgentInstruction(agent_pb2.RunAgentInstructionRequest(instruction=instruction)):
if ev.taskStream.stage == "final":
print("Result:", ev.taskStream.message)最佳实践
- 临时容器配置:运行 Docker 时,避免在镜像中嵌入 API 密钥或模型凭证,确保镜像无敏感信息。
- 按请求传递凭证:在 RunAgentInstructionRequest.runningConfig 中传递 authorizationInfo 和分阶段 LLMConfig(如需则包含 apiKey);仅在可信隔离部署中使用全局配置。
- 持久化存储:优先使用 PostgreSQL 后端,确保任务元数据与统计信息在服务重启后不丢失,且支持审计。
- 水平扩展::多副本部署,避免单点故障。
- 不可变默认配置:将全局 CommonConfig 视为基线,通过任务级配置覆盖,防止跨租户信息泄露。
- 最小权限原则:为不同环境(开发/测试/生产)分配独立密钥;定期轮换密钥,且绝不在镜像中包含生产环境密钥。
- 可观测性:启用 Prometheus 监控,聚合日志,对“失败/取消”状态激增或沙箱创建错误设置告警。
- 优雅取消:在客户端实现超时机制,尽早调用 CancelTask 释放沙箱资源。
- 版本锁定:使用镜像摘要(Digest)指定版本,生产环境避免使用 latest 标签,确保可复现性。
- 沙箱策略:仅对延迟敏感的任务使用预创建沙箱;其他场景允许自动创建沙箱,以获得更好的隔离性。
扩展模式
-
- 水平扩展:在服务后部署多个副本,每个副本管理独立的沙箱实例。
-
- 分片:按项目/组织路由任务至专用部署实例。
-
- 持久化:启用 PostgreSQL 后端,实现 Pod 重启后任务状态恢复。
-
- 可观测性:抓取指标数据,添加日志聚合(标准输出结构化日志)。
安全和密钥管理
- 将 API 密钥存储在密钥管理器中(K8s Secrets、HashiCorp Vault、AWS/GCP 密钥服务)。
- 使用本地后端限制网络出口。
- 每个环境(dev/staging/prod)使用单独的服务账户。
健康检查和就绪探针
- 对 50051 端口执行 gRPC TCP 探针(就绪探针与存活探针)。
- 未来将支持自定义健康检查 RPC,用于更深度的诊断。
- 对沙箱创建错误率激增设置警报。
常见部署问题
| 问题 | 原因 | 修复 |
|---|---|---|
| 沙箱无法绑定 | 缺少 LYBIC_API_KEY / ORG 或配额不足 | 检查环境变量配置,确认账户剩余配额 |
| 冷启动时间长 | 按需创建沙箱(首次创建需耗时) | 使用预创建沙箱(配置 LYBIC_PRECREATE_SID)或预热沙箱池 |
| 任务历史丢失 | 使用内存(memory)作为存储后端 | 切换到 postgres 后端 |
| 指标为空 | 未设置 ENABLE_PROMETHEUS | 设置环境变量 + 打开端口 8000 |
版本升级
拉取新镜像并滚动重启:
kubectl set image deployment/lybic-agent-grpc agent=agenticlybic/guiagent:latest使用固定版本以获得可复现性,在生产环境中避免 latest。
下一步
- 与 MCP 服务器集成,实现基于 LLM 的指令流驱动。
- 后续版本将添加分布式追踪(OpenTelemetry)。
- 构建高层级编排器,协调多个智能体副本工作。