fix(ai): persist context requests and enforce agent execution order
This commit is contained in:
@@ -218,4 +218,30 @@ describe('AgentService', () => {
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('persists a response requesting source or logs even without commands or changes', () => {
|
||||||
|
const service = Object.create(AgentService.prototype) as unknown as {
|
||||||
|
parseSyncedMessage: (value: Record<string, unknown>, index: number) => {
|
||||||
|
plan: { requestedModules: string[]; requestedLogs: string[] };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const message = service.parseSyncedMessage({
|
||||||
|
id: 'agent-plan-context',
|
||||||
|
role: 'assistant',
|
||||||
|
content: '차량 설정 파일을 확인하겠습니다.',
|
||||||
|
plan: {
|
||||||
|
summary: '차량 설정 파일을 확인하겠습니다.',
|
||||||
|
explanation: '조향 방식 설정을 확인하려면 차량 소스가 필요합니다.',
|
||||||
|
requestedModules: ['NAV2-MKMini'],
|
||||||
|
requestedLogs: ['NAV2-Control'],
|
||||||
|
actionPlan: ['차량 설정 확인'],
|
||||||
|
commands: [],
|
||||||
|
changes: [],
|
||||||
|
createModule: null,
|
||||||
|
},
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
expect(message.plan.requestedModules).toEqual(['NAV2-MKMini']);
|
||||||
|
expect(message.plan.requestedLogs).toEqual(['NAV2-Control']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -311,6 +311,11 @@ export class AgentService {
|
|||||||
let plan: AgentPlan | null = null;
|
let plan: AgentPlan | null = null;
|
||||||
if (rawPlan) {
|
if (rawPlan) {
|
||||||
const moduleNames = new Set<string>();
|
const moduleNames = new Set<string>();
|
||||||
|
for (const field of ['requestedModules', 'requestedLogs']) {
|
||||||
|
for (const name of Array.isArray(rawPlan[field]) ? rawPlan[field] : []) {
|
||||||
|
if (typeof name === 'string') moduleNames.add(name.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const command of Array.isArray(rawPlan.commands)
|
for (const command of Array.isArray(rawPlan.commands)
|
||||||
? rawPlan.commands
|
? rawPlan.commands
|
||||||
: []) {
|
: []) {
|
||||||
@@ -744,7 +749,12 @@ export class AgentService {
|
|||||||
'학생이 정보 확인이나 설명만 요청하면 제공된 입력만 근거로 답하고 changes는 빈 배열, createModule은 null로 반환한다.',
|
'학생이 정보 확인이나 설명만 요청하면 제공된 입력만 근거로 답하고 changes는 빈 배열, createModule은 null로 반환한다.',
|
||||||
'작업이 여러 단계라면 actionPlan에 학생이 이해할 수 있는 짧은 단계 목록을 순서대로 작성한다.',
|
'작업이 여러 단계라면 actionPlan에 학생이 이해할 수 있는 짧은 단계 목록을 순서대로 작성한다.',
|
||||||
'빌드, 테스트, 진단 명령 실행이 필요하면 commands에 현재 단계에서 실행할 다음 승인 명령만 넣는다. 명령은 해당 모듈 루트에서 실행된다.',
|
'빌드, 테스트, 진단 명령 실행이 필요하면 commands에 현재 단계에서 실행할 다음 승인 명령만 넣는다. 명령은 해당 모듈 루트에서 실행된다.',
|
||||||
|
'파일 변경과 commands를 함께 반환하면 모든 명령은 파일 변경 적용 후 실행할 검증 명령이어야 하며, actionPlan에도 파일 변경을 명령보다 먼저 적는다.',
|
||||||
|
'파일 변경 전에 실행해야 하는 사전 진단 명령이라면 changes를 비우고 명령만 먼저 반환한다. 실행 결과가 다음 요청에 제공되면 그때 변경안을 제안한다.',
|
||||||
'commands는 읽기, 빌드, 테스트, 실행 진단에 필요한 최소 명령만 제안하고 sudo, 시스템 설정 변경, 삭제, 재부팅, 자격증명 접근 명령은 절대 제안하지 않는다.',
|
'commands는 읽기, 빌드, 테스트, 실행 진단에 필요한 최소 명령만 제안하고 sudo, 시스템 설정 변경, 삭제, 재부팅, 자격증명 접근 명령은 절대 제안하지 않는다.',
|
||||||
|
'ros2 run 또는 ros2 launch는 대상 모듈의 state가 running일 때만 제안한다. 이 명령은 해당 모듈 컨테이너 안에서 최대 20초간 관찰 실행된다.',
|
||||||
|
'ros2 실행이 필요한 대상 모듈의 state가 stopped이면 commands에 command가 정확히 "lms module start"인 모듈 시작 승인 액션 하나만 반환한다. 학생이 승인하면 LMS가 모듈을 시작하고 결과를 다음 요청에 제공한다.',
|
||||||
|
'모듈 시작 액션이 승인되기 전에는 ros2 run 또는 ros2 launch를 함께 제안하지 않는다. ros2 topic pub, service call, action send_goal 같은 ROS2 제어 명령은 제안하지 않는다.',
|
||||||
'명령에는 cd를 포함하지 말고 source 연결은 &&만 사용한다. 한 명령은 한 줄로 작성한다.',
|
'명령에는 cd를 포함하지 말고 source 연결은 &&만 사용한다. 한 명령은 한 줄로 작성한다.',
|
||||||
'기존 모듈 변경은 전달받은 moduleName만 사용한다. 새 모듈 요청일 때만 createModule을 채운다.',
|
'기존 모듈 변경은 전달받은 moduleName만 사용한다. 새 모듈 요청일 때만 createModule을 채운다.',
|
||||||
'launchFile은 모듈 루트 기준 상대 경로다. 파일 path도 각 모듈 루트 기준 상대 경로다.',
|
'launchFile은 모듈 루트 기준 상대 경로다. 파일 path도 각 모듈 루트 기준 상대 경로다.',
|
||||||
|
|||||||
Reference in New Issue
Block a user