GitLab Duo Agent Platform
GitLab v19.2이 가이드는 GitLab Duo Agent Platform으로 작업하는 방법을 설명합니다. GitLab Duo Agent Platform은 AI 기반 자동화 기능을 위한 통합 인터페이스를 제공하는 Vue.js로 빌드된 단일 페이지 애플리케이션(SPA)입니다.
이 가이드는 GitLab Duo Agent Platform으로 작업하는 방법을 설명합니다.
개요#
GitLab Duo Agent Platform은 AI 기반 자동화 기능을 위한 통합 인터페이스를 제공하는 Vue.js로 빌드된 단일 페이지 애플리케이션(SPA)입니다. 이 플랫폼은 여러 내비게이션 항목이 /automate 경로 하위에 공존할 수 있도록 하는 범위가 지정된 라우팅 시스템을 사용합니다.
이 플랫폼은 컴포넌트 매핑 시스템을 통해 컨텍스트별 기능을 제공하면서 동일한 프런트엔드 인프라를 다양한 컨텍스트(프로젝트, 그룹 등)에서 재사용할 수 있도록 하는 유연한 네임스페이스 시스템으로 설계되었습니다.
네임스페이스 아키텍처#
네임스페이스 시스템은 다음을 수행하는 중앙 매핑 메커니즘을 중심으로 구축됩니다:
-
네임스페이스 확인 - 플랫폼이 실행 중인 컨텍스트를 결정합니다
-
Vue 컴포넌트에 매핑 - 해당 네임스페이스에 적합한 Vue 컴포넌트로 라우팅합니다
-
GraphQL 쿼리를 props로 전달 - 의존성 주입을 통해 네임스페이스별 데이터를 제공합니다
진입점#
주요 진입점은 다음 위치에 있습니다:
ee/app/assets/javascripts/pages/projects/duo_agents_platform/index.js
이 파일은 플랫폼을 가져오고 초기화합니다:
import { initDuoAgentsPlatformProjectPage } from 'ee/ai/duo_agents_platform/namespace/project';
initDuoAgentsPlatformProjectPage();
앱 구조#
소스 코드 보기
graph TD
A[Entry Point] --> B[initDuoAgentsPlatformPage]
B --> C[Extract Namespace Data]
C --> D[Create Router with Namespace]
D --> E[Component Mapping]
E --> F[Namespace-Specific Component]
F --> G[GraphQL Query with Props]
G --> H[Rendered UI]
I[Dataset Properties] --> C
J[Namespace Constant] --> E
K[Component Mappings] --> E</code></pre></details></div>
새 내비게이션 항목 추가#
GitLab Duo Agent Platform은 Vue Router 구성이 브레드크럼 내비게이션을 직접 구동하는 라우터 기반 내비게이션 시스템을 사용합니다. 핵심은 ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js의 라우터 구조가 URL 구조와 브레드크럼 계층 구조 모두를 결정한다는 것입니다.
라우터 기반 내비게이션 작동 방식#
시스템은 다음 상호 연결된 컴포넌트를 통해 작동합니다:
- 라우터 구조:
meta.text 속성이 있는 중첩 라우트가 브레드크럼 레이블을 정의합니다
- 브레드크럼 컴포넌트:
duo_agents_platform_breadcrumbs.vue가 일치된 라우트에서 브레드크럼을 자동으로 생성합니다
- 라우트 주입:
index.js의 injectVueAppBreadcrumbs()가 라우터를 브레드크럼 시스템에 연결합니다
라우터 분석#
ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js의 현재 라우터 구조를 보면:
routes: [
{
component: NestedRouteApp, // Simple <router-view /> wrapper
path: '/agent-sessions',
meta: {
text: s__('DuoAgentsPlatform|Sessions'), // This becomes a breadcrumb
},
children: [
{
name: AGENTS_PLATFORM_INDEX_ROUTE,
path: '', // Matches /agent-sessions exactly
component: AgentsPlatformIndex,
},
{
name: AGENTS_PLATFORM_NEW_ROUTE,
path: 'new', // Matches /agent-sessions/new
component: AgentsPlatformNew,
meta: {
text: s__('DuoAgentsPlatform|New'), // This becomes a breadcrumb
},
},
// ...
],
},
];
브레드크럼 생성#
브레드크럼 컴포넌트(duo_agents_platform_breadcrumbs.vue)는 다음과 같이 작동합니다:
this.$route.matched에서 일치된 모든 라우트를 가져옵니다
- 일치된 각 라우트에서
meta.text를 추출합니다
- 텍스트와 라우트 경로로 브레드크럼 항목을 생성합니다
- 정적 브레드크럼("Automate" 등)과 결합합니다
// duo_agents_platform_breadcrumbs.vue에서
const matchedRoutes = (this.$route?.matched || [])
.map((route) => {
return {
text: route.meta?.text, // Uses meta.text for breadcrumb label
to: { path: route.path },
};
})
.filter((r) => r.text); // Only routes with meta.text become breadcrumbs
새 내비게이션 항목 추가#
새 최상위 내비게이션 항목("Your Feature" 등)을 추가하려면 라우터에 새 라우트 트리를 추가해야 합니다:
1단계: 라우트 상수 추가#
파일: ee/app/assets/javascripts/ai/duo_agents_platform/router/constants.js
// 기능에 대한 라우트 이름 상수 추가
export const AGENTS_PLATFORM_YOUR_FEATURE_INDEX = 'your_feature_index';
export const AGENTS_PLATFORM_YOUR_FEATURE_NEW = 'your_feature_new';
export const AGENTS_PLATFORM_YOUR_FEATURE_SHOW = 'your_feature_show';
2단계: 라우터에 라우트 추가#
파일: ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js
import YourFeatureIndex from '../pages/your_feature/your_feature_index.vue';
import YourFeatureNew from '../pages/your_feature/your_feature_new.vue';
import YourFeatureShow from '../pages/your_feature/your_feature_show.vue';
export const createRouter = (base, namespace) => {
return new VueRouter({
base,
mode: 'history',
routes: [
// 기존 agent-sessions 라우트
{
component: NestedRouteApp,
path: '/agent-sessions',
meta: {
text: s__('DuoAgentsPlatform|Sessions'),
},
children: [
{
name: AGENTS_PLATFORM_INDEX_ROUTE,
path: '',
component: getNamespaceIndexComponent(namespace),
},
// ... 기존 자식들
],
},
// 새로운: 기능 라우트
{
component: NestedRouteApp,
path: '/your-feature', // URL 경로가 됨
meta: {
text: s__('DuoAgentsPlatform|Your Feature'), // 브레드크럼이 됨
},
children: [
{
name: AGENTS_PLATFORM_YOUR_FEATURE_INDEX,
path: '', // /your-feature와 정확히 일치
component: YourFeatureIndex,
},
{
name: AGENTS_PLATFORM_YOUR_FEATURE_NEW,
path: 'new', // /your-feature/new와 일치
component: YourFeatureNew,
meta: {
text: s__('DuoAgentsPlatform|New'), // 브레드크럼: "Your Feature > New"
},
},
{
name: AGENTS_PLATFORM_YOUR_FEATURE_SHOW,
path: ':id(\\d+)', // /your-feature/123과 일치
component: YourFeatureShow,
// meta.text 없음 - 라우트 파라미터를 브레드크럼 텍스트로 사용
},
],
},
{ path: '*', redirect: '/agent-sessions' },
],
});
};
3단계: 백엔드 라우트 추가(필요한 경우)#
ee/config/routes/project.rb의 기존 와일드카드 라우트가 새 경로를 처리해야 합니다:
scope :automate do
get '/(*vueroute)' => 'duo_agents_platform#show', as: :automate, format: false
end
중요: 사이드바 메뉴 항목(4단계)을 추가하는 경우 명명된 라우트 헬퍼를 추가해야 합니다:
scope :automate do
get '/(*vueroute)' => 'duo_agents_platform#show', as: :automate, format: false
# 사이드바 메뉴 헬퍼를 위한 명명된 라우트
get 'agent-sessions', to: 'duo_agents_platform#show', as: :automate_agent_sessions, format: false
get 'your-feature', to: 'duo_agents_platform#show', as: :automate_your_features, format: false
end
참고: 라우트 이름에는 복수형을 사용합니다(예: automate_your_features). 기존 패턴과 일치시키고 Rails 경로 헬퍼가 올바르게 생성되도록 합니다.
4단계: 사이드바 메뉴 항목 추가#
파일: ee/lib/sidebars/projects/super_sidebar_menus/duo_agents_menu.rb
configure_menu_items 메서드에 메뉴 항목을 추가하고 해당 메뉴 항목 메서드를 생성합니다:
override :configure_menu_items
def configure_menu_items
add_item(duo_agents_runs_menu_item)
add_item(duo_agents_your_feature_menu_item) # 새 메뉴 항목 추가
true
end
private
def duo_agents_your_feature_menu_item
::Sidebars::MenuItem.new(
title: s_('Your Feature'),
link: project_automate_your_features_path(context.project), # 참고: 복수형 'features'
active_routes: { controller: :duo_agents_platform },
item_id: :agents_your_feature
)
end
5단계: Vue 컴포넌트 생성#
파일: ee/app/assets/javascripts/ai/duo_agents_platform/pages/your_feature/your_feature_index.vue
<script>
export default {
name: 'YourFeatureIndex',
};
</script>
<template>
<div>
<h1>Your Feature</h1>
</div>
</template>
새 네임스페이스 추가#
새 네임스페이스를 추가하려면(예: 그룹용):
1. 네임스페이스 상수 정의#
// ee/app/assets/javascripts/ai/duo_agents_platform/constants.js
export const AGENT_PLATFORM_GROUP_PAGE = 'group';
2. 네임스페이스 디렉토리 구조 생성#
ee/app/assets/javascripts/ai/duo_agents_platform/namespace/group/
├── index.js
├── group_agents_platform_index.vue
└── graphql/
└── queries/
└── get_group_agent_flows.query.graphql
3. 네임스페이스 초기화 구현#
// ee/app/assets/javascripts/ai/duo_agents_platform/namespace/group/index.js
import { initDuoAgentsPlatformPage } from '../../index';
import { AGENT_PLATFORM_GROUP_PAGE } from '../../constants';
export const initDuoAgentsPlatformGroupPage = () => {
initDuoAgentsPlatformPage({
namespace: AGENT_PLATFORM_GROUP_PAGE,
namespaceDatasetProperties: ['groupPath', 'groupId'],
});
};
4. 네임스페이스별 컴포넌트 생성#
<!-- ee/app/assets/javascripts/ai/duo_agents_platform/namespace/group/group_agents_platform_index.vue -->
<script>
import getGroupAgentFlows from './graphql/queries/get_group_agent_flows.query.graphql';
import DuoAgentsPlatformIndex from '../../pages/index/duo_agents_platform_index.vue';
export default {
components: { DuoAgentsPlatformIndex },
inject: ['groupPath'], // 그룹별 주입
apollo: {
workflows: {
query: getGroupAgentFlows, // 그룹별 쿼리
variables() {
return {
groupPath: this.groupPath,
// ...
};
},
// ...
},
},
};
</script>
<template>
<duo-agents-platform-index
:is-loading-workflows="isLoadingWorkflows"
:workflows="workflows"
:workflows-page-info="workflowsPageInfo"
:workflow-query="$apollo.queries.workflows"
/>
</template>
5. 컴포넌트 매핑 업데이트#
// ee/app/assets/javascripts/ai/duo_agents_platform/router/utils.js
import { AGENT_PLATFORM_PROJECT_PAGE, AGENT_PLATFORM_GROUP_PAGE } from '../constants';
import ProjectAgentsPlatformIndex from '../namespace/project/project_agents_platform_index.vue';
import GroupAgentsPlatformIndex from '../namespace/group/group_agents_platform_index.vue';
export const getNamespaceIndexComponent = (namespace) => {
const componentMappings = {
[AGENT_PLATFORM_PROJECT_PAGE]: ProjectAgentsPlatformIndex,
[AGENT_PLATFORM_GROUP_PAGE]: GroupAgentsPlatformIndex, // 새 매핑
};
return componentMappings[namespace];
};
6. 진입점 생성#
// ee/app/assets/javascripts/pages/groups/duo_agents_platform/index.js
import { initDuoAgentsPlatformGroupPage } from 'ee/ai/duo_agents_platform/namespace/group';
initDuoAgentsPlatformGroupPage();
이 아키텍처의 장점#
-
코드 재사용: 동일한 프런트엔드 인프라가 다양한 컨텍스트에서 작동합니다
-
관심사 분리: 각 네임스페이스가 자체 데이터 가져오기 및 비즈니스 로직을 처리합니다
-
확장성: 기존 코드를 수정하지 않고 새 네임스페이스를 쉽게 추가할 수 있습니다
-
타입 안전성: 네임스페이스 상수 및 필수 속성을 통한 명확한 계약
-
유지 관리성: 격리된 네임스페이스 구현이 결합도를 줄입니다
주요 구현 세부 사항#
라우터 기반 브레드크럼#
브레드크럼 시스템은 라우터 구조에서 내비게이션을 자동으로 생성합니다:
meta.text가 있는 부모 라우트는 브레드크럼 세그먼트가 됩니다
meta.text가 있는 자식 라우트는 브레드크럼 체인을 확장합니다
meta.text가 없는 라우트는 라우트 파라미터(:id 등)를 브레드크럼 텍스트로 사용합니다
- 정적 브레드크럼("Automate" 등)은 모든 라우트 앞에 추가됩니다
URL 구조#
각 최상위 내비게이션 항목에는 고유한 URL 네임스페이스가 있습니다:
- Sessions:
/automate/agent-sessions, /automate/agent-sessions/new, /automate/agent-sessions/123
- Your Feature:
/automate/your-feature, /automate/your-feature/new, /automate/your-feature/456
중첩 라우트 패턴#
플랫폼은 일관된 중첩 라우트 패턴을 사용합니다:
- 부모 라우트: URL 경로와 최상위 브레드크럼을 정의합니다
- NestedRouteApp 컴포넌트: 자식 라우트를 위한 단순한
<router-view /> 래퍼
- 자식 라우트: 특정 페이지와 추가 브레드크럼 세그먼트를 정의합니다
예시: 현재 에이전트 세션 구현#
기존 에이전트 세션 기능이 이 패턴을 보여줍니다:
{
component: NestedRouteApp, // 자식 라우트를 렌더링
path: '/agent-sessions', // URL: /automate/agent-sessions
meta: {
text: s__('DuoAgentsPlatform|Sessions'), // 브레드크럼: "Sessions"
},
children: [
{
name: AGENTS_PLATFORM_INDEX_ROUTE,
path: '', // URL: /automate/agent-sessions
component: AgentsPlatformIndex, // 추가 브레드크럼 없음
},
{
name: AGENTS_PLATFORM_NEW_ROUTE,
path: 'new', // URL: /automate/agent-sessions/new
component: AgentsPlatformNew,
meta: {
text: s__('DuoAgentsPlatform|New'), // 브레드크럼: "Sessions > New"
},
},
{
name: AGENTS_PLATFORM_SHOW_ROUTE,
path: ':id(\\d+)', // URL: /automate/agent-sessions/123
component: AgentsPlatformShow, // 브레드크럼: "Sessions > 123"
// meta.text 없음 - :id 파라미터를 브레드크럼으로 사용
},
],
}
이는 다음 브레드크럼 계층 구조를 생성합니다:
/automate/agent-sessions → "Automate > Sessions"
/automate/agent-sessions/new → "Automate > Sessions > New"
/automate/agent-sessions/123 → "Automate > Sessions > 123"
주요 파일 참조#
- 진입점:
ee/app/assets/javascripts/pages/projects/duo_agents_platform/index.js
- 주요 초기화:
ee/app/assets/javascripts/ai/duo_agents_platform/index.js
- 라우터:
ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js
- 컴포넌트 매핑:
ee/app/assets/javascripts/ai/duo_agents_platform/router/utils.js
- 상수:
ee/app/assets/javascripts/ai/duo_agents_platform/constants.js
- 유틸리티:
ee/app/assets/javascripts/ai/duo_agents_platform/utils.js
- 프로젝트 네임스페이스:
ee/app/assets/javascripts/ai/duo_agents_platform/namespace/project/
모범 사례#
- 라우터 구조: 브레드크럼 계층 구조를 정의하기 위해
meta.text 속성이 있는 중첩 라우트 사용
- URL 네이밍: URL 경로에 kebab-case 사용(
/your-feature, /yourFeature 아님)
- 라우트 이름: 라우트 이름에 설명적인 상수 사용(
AGENTS_PLATFORM_YOUR_FEATURE_INDEX)
- 컴포넌트 구성:
pages/[feature]/ 디렉토리에 기능 컴포넌트 배치
- 브레드크럼 텍스트: 모든
meta.text 값에 국제화 문자열(s__()) 사용
- 일관된 패턴:
NestedRouteApp으로 기존 중첩 라우트 패턴 따르기
- 네임스페이스 분리:
namespace/ 하위 전용 디렉토리에 네임스페이스별 로직 유지
- 컴포넌트 매핑: 컴포넌트 매핑 시스템을 사용하여 네임스페이스별 컴포넌트로 라우팅
- 데이터 주입: 네임스페이스별 데이터에 Vue의 provide/inject 패턴 사용
트러블슈팅#
일반적인 문제#
- 브레드크럼이 표시되지 않음: 부모 라우트에
meta.text 속성이 있는지 확인
- 라우트가 작동하지 않음:
ee/config/routes/project.rb에 와일드카드 라우트가 있는지 확인
- 사이드바가 강조 표시되지 않음: 사이드바 메뉴 항목에 올바른
active_routes가 있는지 확인
- 404 오류: 라우트 경로가 기존 라우트와 충돌하지 않는지 확인
디버깅 팁#
- Vue DevTools:
$route.matched를 검사하여 어떤 라우트가 일치하는지 확인
- 라우터 상태: Vue DevTools에서 라우터 구성 확인
- 브레드크럼 디버그: 브레드크럼 컴포넌트에
console.log(this.$route.matched) 추가
- URL 테스트: 브라우저에서 URL로 직접 이동하여 라우트 테스트
가짜 플로우 생성#
플랫폼을 테스트하기 위한 가짜 플로우를 생성하려면 ee/lib/tasks/gitlab/duo_workflow/duo_workflow.rake Rake 작업을 실행할 수 있습니다.
특정 사용자가 특정 프로젝트에서 만든 50개의 플로우 중 20개를 만드는 예시:
bundle exec rake "gitlab:duo_workflow:populate[50,20,user@example.com,gitlab-org/gitlab-test]