←   Back to projectsCASE STUDY   /   03

MULTI-AGENT TRAVEL SYSTEM

TRAVEL ASSISTANT

A Plan-and-Execute travel assistant that coordinates intent recognition, itinerary planning, enterprise-policy RAG, live information lookup, and persistent user preferences.

6intent types
2memory layers
10/10QA scenarios completed
RAGpolicy retrieval

A useful travel assistant must handle changing user intent, external information, company rules, multi-step planning, and long-term preferences without allowing one failed tool call to collapse the entire task.

01 / SYSTEM MAP

A request becomes a plan, not a single prompt

The assistant first classifies intent, then executes specialized skills in priority groups. Policy retrieval, live lookup, preference handling and itinerary planning can contribute to one response.
01

Request

Receive the user message plus recent and persistent context.

02

Intention

Detect one or more of six intent types and extract required entities.

03

Plan

Build a priority-aware agent schedule.

POLICY RAGLIVE INFOPREFERENCEITINERARY
04

Execute

Lazy-load skills and run equal-priority tasks concurrently.

05

Aggregate

Normalize partial results without collapsing the whole request on one failure.

06

Remember

Write useful preferences and trip facts into the long-term layer.

02 / REAL BEHAVIOR TRACE

Preference memory changes a later itinerary

One QA sequence first stored explicit preferences and then requested a new trip. The later itinerary reused those preferences instead of asking for them again.
memory_trace.json
INPUT
Remember Marriott / Hilton, Air China / China Eastern, window seat, Beijing Chaoyang.
MEMORY WRITE
hotel · airline · seat · home_location
LATER REQUEST
Plan a five-day business trip to Shanghai.
REUSED CONTEXT
Preferred hotels, airlines, seat and Beijing origin appeared in the itinerary.
EVIDENCE TRACE

Short-term: recent dialogue, capped by turn count

Long-term: JSON-backed preferences and trip history

Agent context: selected memory is injected before execution

03 / SELECTED IMPLEMENTATION

Orchestration, loading and failure recovery

These excerpts come from the working Python project—not a diagram-only concept.
agents/orchestration_agent.pyPriority-group parallel execution+

Agents at the same priority run through asyncio.gather; exceptions are returned as partial failures instead of crashing aggregation.

async def _execute_parallel_agents(
    self, tasks, context, previous_results
):
    parallel_coroutines = []
    for task in tasks:
        agent_name = task.get("agent_name")
        priority = task.get("priority", 0)
        coroutine = self._execute_agent(
            agent_name=agent_name,
            context=context,
            reason=task.get("reason", ""),
            expected_output=task.get("expected_output", ""),
            previous_results=previous_results,
        )
        parallel_coroutines.append((agent_name, priority, coroutine))

    execution_results = await asyncio.gather(
        *[coro for _, _, coro in parallel_coroutines],
        return_exceptions=True,
    )

    results = []
    for (agent_name, priority, _), exec_result in zip(
        parallel_coroutines, execution_results
    ):
        # Exceptions are converted to structured partial failures here.
        results.append({
            "agent_name": agent_name,
            "priority": priority,
            "result": exec_result,
        })
    return results
agents/lazy_agent_registry.pySkills load only when requested+

The registry discovers plugin agents, resolves legacy names, imports the matching module and caches the instance after first use.

def __getitem__(self, agent_name: str):
    if agent_name in self.cache:
        return self.cache[agent_name]

    skill_name = self._resolve_agent_name(agent_name)
    script_path = self._skill_map[skill_name]
    module_name = f"skills.{skill_name}.agent"
    spec = importlib.util.spec_from_file_location(module_name, script_path)
    module = importlib.util.module_from_spec(spec)
    sys.modules[module_name] = module
    spec.loader.exec_module(module)

    for name, obj in inspect.getmembers(module):
        if inspect.isclass(obj) and issubclass(obj, AgentBase):
            agent_class = obj
            break

    agent_instance = agent_class(**init_params)
    self.cache[agent_name] = agent_instance
    return agent_instance
utils/circuit_breaker.pyClosed → open → half-open recovery+

Repeated failures stop further model calls for a recovery window, then allow controlled probing instead of cascading failure.

class CircuitBreaker:
    def record_failure(self) -> None:
        if self._state == CircuitState.HALF_OPEN:
            self._state = CircuitState.OPEN
            self._opened_at = time.monotonic()
            return

        self._failure_count += 1
        if self._failure_count >= self.failure_threshold:
            self._state = CircuitState.OPEN
            self._opened_at = time.monotonic()

    def raise_if_open(self) -> None:
        if not self.allow_call():
            raise CircuitOpenError("服务暂时不可用,请稍后再试")

04 / QA EVIDENCE

The test report is useful because it exposes failure—not because it says 10/10

The latest runner completed all ten prompts in 1,288.6 seconds. Reviewing the actual answers found one safe no-answer and one downstream generation failure. The original “success” counter only meant the outer test raised no exception.
qa_test_20260309_013545.md

total_questions  10

runner_completed 10

total_time_sec  1288.6

review_status   QUALITY AUDIT REQUIRED

10runner completions
1safe no-answer
1surfaced generation failure
128.9saverage per prompt
  • The latest project QA report completed all 10 defined end-to-end scenarios.
  • The system supports six intent types with a two-layer memory design.
  • The architecture demonstrates applied AI engineering beyond prompt writing: orchestration, retrieval, memory, tools, and failure recovery.
The 10/10 figure refers to completion of the project’s defined QA scenarios, not a controlled benchmark of answer quality.