Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,23 @@ res = asyncio.run(agent.run("hello!"))
print(res)
```

## AgentKit application

Use the shared AgentKit application factory when your project needs AgentKit
APIs, VeADK's bundled Web UI, health checks, and agent-topology endpoints. This
keeps platform routes and lifecycle code out of your agent module:

```python
from veadk import Agent
from veadk.integrations.agentkit import create_agentkit_app

root_agent = Agent(name="customer_support")
app = create_agentkit_app(root_agent)
```

See [`examples/generated_agentkit_project`](examples/generated_agentkit_project)
for a complete generated project.

## Feishu bot channel

VeADK now provides `veadk.extensions.FeishuChannelExtension` for bridging a Feishu bot with a `Runner`. It maps `union_id` to `user_id`, and `thread_id` / `chat_id` to `session_id`, so VeADK memory and tracing can work directly in Feishu conversations.
Expand Down
23 changes: 23 additions & 0 deletions docs/content/docs/framework/agentkit.en.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,29 @@ Besides deploying to VeFaaS, you can deploy your agent to the Volcengine **Agent
- VeADK is installed (it bundles the `agentkit` dependency).
- Volcengine access credentials (Access Key / Secret Key) are configured in the `.env` file at your project root.

## Shared application component

New projects generated by Studio contain only agent business logic and a small
`app.py`. `veadk.integrations.agentkit` provides the AgentKit APIs, health
check, agent topology, bundled Web UI, default short-term memory, and optional
Feishu lifecycle:

```python
from agents.my_agent.agent import AGENT_DISPLAY_NAMES, root_agent
from veadk.integrations.agentkit import create_agentkit_app

app = create_agentkit_app(
root_agent,
AGENT_DISPLAY_NAMES,
)
```

The resulting service exposes AgentKit conversation APIs together with
`/ping`, `/web/agent-info/{app_name}`, `/web/agent-graph`, and the bundled Web
UI. See
[`examples/generated_agentkit_project`](https://github.com/volcengine/veadk-python/tree/main/examples/generated_agentkit_project)
for a complete generated project.

## Steps

<Steps>
Expand Down
20 changes: 20 additions & 0 deletions docs/content/docs/framework/agentkit.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ title: "部署到 AgentKit"
- 已安装 VeADK(包含 `agentkit` 依赖)。
- 已在项目根目录的 `.env` 文件中配置火山引擎访问凭据(Access Key / Secret Key)。

## 公共应用组件

通过 Studio 新生成的项目只保留 Agent 业务代码和一个精简的 `app.py`。AgentKit
接口、健康检查、Agent 拓扑、内置 Web UI、短期记忆默认配置和可选的飞书生命周期
由 `veadk.integrations.agentkit` 统一提供:

```python
from agents.my_agent.agent import AGENT_DISPLAY_NAMES, root_agent
from veadk.integrations.agentkit import create_agentkit_app

app = create_agentkit_app(
root_agent,
AGENT_DISPLAY_NAMES,
)
```

服务会提供 AgentKit 对话接口,以及 `/ping`、`/web/agent-info/{app_name}`、
`/web/agent-graph` 和内置 Web UI。完整生成结果可参考
[`examples/generated_agentkit_project`](https://github.com/volcengine/veadk-python/tree/main/examples/generated_agentkit_project)。

## 步骤

<Steps>
Expand Down
8 changes: 8 additions & 0 deletions examples/generated_agentkit_project/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copy this file to .env and replace the placeholder.

# Required Volcengine Ark API key.
MODEL_AGENT_API_KEY=your-ark-api-key
# Model configuration.
MODEL_AGENT_NAME=doubao-seed-1-6-250615
MODEL_AGENT_PROVIDER=openai
MODEL_AGENT_API_BASE=https://ark.cn-beijing.volces.com/api/v3/
19 changes: 19 additions & 0 deletions examples/generated_agentkit_project/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated AgentKit project

This example shows the output of VeADK Studio for a root customer-support Agent
with one order assistant. Business logic lives under `agents/`; `app.py` only
connects the Agent to VeADK's shared AgentKit application component.

## Run

```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# Set MODEL_AGENT_API_KEY in .env.
python app.py
```

The service listens on `0.0.0.0:8000` by default. Override it with the `HOST`
and `PORT` environment variables.
13 changes: 13 additions & 0 deletions examples/generated_agentkit_project/agents/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from .agent import AGENT_DISPLAY_NAMES, root_agent

__all__ = ["AGENT_DISPLAY_NAMES", "root_agent"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from veadk import Agent

INSTRUCTION_AGENT = (
"""Route order questions to the order assistant and answer general questions."""
)

INSTRUCTION_AGENT_SUB_1 = """Help the user check and understand an order."""

agent_sub_1 = Agent(
name="order_assistant",
description="Handles order questions.",
instruction=INSTRUCTION_AGENT_SUB_1,
)

agent = Agent(
name="customer_support",
description="A generated customer-support multi-agent example.",
instruction=INSTRUCTION_AGENT,
sub_agents=[agent_sub_1],
)

AGENT_DISPLAY_NAMES = {
"customer_support": "customer_support",
"order_assistant": "order_assistant",
}

# ADK requires the top-level agent to be exported as root_agent.
root_agent = agent
25 changes: 25 additions & 0 deletions examples/generated_agentkit_project/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from agents.customer_support.agent import AGENT_DISPLAY_NAMES, root_agent
from veadk.integrations.agentkit import create_agentkit_app, run_agentkit_app

app = create_agentkit_app(
root_agent,
AGENT_DISPLAY_NAMES,
enable_feishu=False,
)

if __name__ == "__main__":
run_agentkit_app(app)
4 changes: 4 additions & 0 deletions examples/generated_agentkit_project/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
veadk-python>=1.0.5
agentkit-sdk-python
google-adk
starlette<1.0.0
46 changes: 29 additions & 17 deletions tests/cli/test_generated_agent_backend_codegen_extended.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,27 +47,26 @@
)


# These hashes were produced from the legacy frontend generator before backend
# codegen became the trusted implementation. They intentionally lock the full
# generated file contents, not just Python syntax or selected snippets.
# These hashes lock the complete generated project contents, not just Python
# syntax or selected snippets.
_MINIMAL_FRONTEND_GOLDEN = {
"app.py": "76fb40c6016bf2b70e35012d050b6a0e5be36233b6044e83e1002a3f71150bfb",
"agents/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"agents/demo_agent/agent.py": "6e5bbd448439c7f50f5400227680130e897dd71e8251cb4eb4594c98f4889589",
"agents/demo_agent/__init__.py": "acb6368da255ff70d6760c00085c893aa0b2d973768d293a22bb7181fb1e3448",
"app.py": "c7807c570167793fc8c5a8a72e9f3c32aac0820db3098cb52edb1488c34a8e5f",
"agents/__init__.py": "a6449a6cac3bfda8b834ea39ea95ca2f8d0471ac480e1e876313d7398eea59ba",
"agents/demo_agent/agent.py": "b2d22094a8ea61e8ab6e2b633d7695c5fa5e883f03516cb8771e7ec00be0fe1f",
"agents/demo_agent/__init__.py": "62d651c229ddd771cf0cc0a8b0e05e96b739a737fe71e41fe8bf1df484150c36",
".env.example": "1cdb6e1bfe38616d5d46095ba88ba76a0c189f3d2999bf0dd23b7145ce103ab2",
"requirements.txt": "a7bb29cb47b916a81b626907fcdf84eed525ca22b4214ddc82f96a5ba87c8cc8",
"README.md": "16cbec845b595949c071f3bcf4c056d862b9e2277c00e5d23649b5540dfde83e",
"requirements.txt": "9a04e5f16e94d5e751681082776f1c99f13da7a577c8753c3835e0ea507245e4",
"README.md": "a34208314cf9061c02662028d7a9dd97448e6b73c1d732cb4aeaa8f70dbbc684",
}

_FULL_FRONTEND_GOLDEN = {
"app.py": "bce061ef2a9db0ba8fa2c82c94ae682f166d96bca4d580e99fcaacca71b9a682",
"agents/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"agents/full_agent/agent.py": "d77ee467c4895b1a160927fb551a65d424b68319fc2941014dc5d499edbace68",
"agents/full_agent/__init__.py": "acb6368da255ff70d6760c00085c893aa0b2d973768d293a22bb7181fb1e3448",
"app.py": "a9903cf7e095733e9b8658182a0954a81d8a98b431f8ab995ce3818950127006",
"agents/__init__.py": "a6449a6cac3bfda8b834ea39ea95ca2f8d0471ac480e1e876313d7398eea59ba",
"agents/full_agent/agent.py": "77c6cc42f8f9a99aba060fc93a7a615655b897e6136d6f5325f035e87edce141",
"agents/full_agent/__init__.py": "62d651c229ddd771cf0cc0a8b0e05e96b739a737fe71e41fe8bf1df484150c36",
".env.example": "cb35eed98b4155c755df934f61ca6760293d59508de0a6090632e44501f82748",
"requirements.txt": "5230e5c9a20b97dc95cc753247b4240d7401d9f9b46aa62da851c91552061ba7",
"README.md": "ce6e5ada2031657b5de320465a34cb8c066c6c4181ab111dfb40299d3ec0bcd0",
"requirements.txt": "65b301155863e56165c5777301c3476d0c0b68e569fff4450f454e36fd66225d",
"README.md": "1bf4dc889c7d1076f50784d253b53412ba7c49bcb69a5d948f9092dbbecb18ac",
}


Expand Down Expand Up @@ -184,8 +183,21 @@ def test_codegen_preserves_agent_display_names_for_topology() -> None:

assert "'agent': '客服智能体'" in agent_py
assert "'agent_sub_1': '订单助手'" in agent_py
assert '"id": agent_id' in app_py
assert '"name": AGENT_DISPLAY_NAMES.get(agent_id, agent_id)' in app_py
assert "create_agentkit_app(" in app_py
assert "AGENT_DISPLAY_NAMES" in app_py
assert 'app.get("/web/agent-info' not in app_py
assert len(app_py.splitlines()) == 25


def test_codegen_enables_feishu_without_exposing_lifecycle_code() -> None:
project = generate_project_from_draft(
AgentDraft(name="demo", deployment={"feishuEnabled": True})
)
app_py = _file_map(project)["app.py"]

assert "enable_feishu=True" in app_py
assert "FeishuChannelExtension" not in app_py
assert "asynccontextmanager" not in app_py


def test_frontend_complete_shape_is_accepted_and_unknown_field_is_rejected() -> None:
Expand Down
Loading
Loading