Spaces:
Running
Running
| import os | |
| import logging | |
| from typing import Any, Dict | |
| from llama_index.core.agent.workflow import ReActAgent | |
| from llama_index.core.tools import FunctionTool | |
| from llama_index.core.workflow import Context | |
| from llama_index.llms.google_genai import GoogleGenAI | |
| # ----------------------------------------------------------------------------- | |
| # Context helper tools --------------------------------------------------------- | |
| # ----------------------------------------------------------------------------- | |
| async def write_state(ctx: Context, key: str, value: Any) -> str: | |
| state_dict = await ctx.get("state") | |
| state_dict[key] = value | |
| await ctx.set("state", state_dict) | |
| return f"state['{key}'] written" | |
| async def read_state(ctx: Context, key: str) -> Any: | |
| state_dict = await ctx.get("state") | |
| return state_dict.get(key, "") | |
| write_state_tool = FunctionTool.from_defaults( | |
| fn=write_state, | |
| name="write_state", | |
| description="Store or overwrite a value in the shared workflow state.", | |
| ) | |
| read_state_tool = FunctionTool.from_defaults( | |
| fn=read_state, | |
| name="read_state", | |
| description="Retrieve a value from the shared workflow state.", | |
| ) | |
| # ----------------------------------------------------------------------------- | |
| # Fresh implementation of answer_question ------------------------------------- | |
| # ----------------------------------------------------------------------------- | |
| def answer_question(question: str) -> str: | |
| """Return chain‑of‑thought and FINAL ANSWER following strict template.""" | |
| gemini_api_key = os.getenv("GEMINI_API_KEY") | |
| if not gemini_api_key: | |
| logging.warning("GEMINI_API_KEY not set – returning fallback answer.") | |
| return f"Chain of thought: (api key missing)\n\nFINAL ANSWER: {question}" | |
| meta_prompt = ( | |
| "You are a professional assistant. Respond with two sections:"\ | |
| "\n1. Chain of thought: concise reasoning (3–5 sentences)."\ | |
| "\n2. FINAL ANSWER: the concise answer following these rules:"\ | |
| "\n • If numeric, no thousands separators or units unless requested."\ | |
| "\n • If text, as few words as possible, no unnecessary articles."\ | |
| "\n • If list, comma‑separate applying the above rules."\ | |
| "\n • Must start exactly with 'FINAL ANSWER:' (uppercase)."\ | |
| f"\n\nQuestion: {question}\n\nAnswer:" | |
| ) | |
| llm = GoogleGenAI(api_key=gemini_api_key, model="gemini-2.5-pro-preview-03-25", temperature=0.05) | |
| return llm.complete(meta_prompt).text.strip() | |
| answer_question_tool = FunctionTool.from_defaults( | |
| fn=answer_question, | |
| name="answer_question", | |
| description="Generate reasoning and emit 'FINAL ANSWER: ...' following the strict format rules.", | |
| ) | |
| # ----------------------------------------------------------------------------- | |
| # System prompt (unchanged) ---------------------------------------------------- | |
| # ----------------------------------------------------------------------------- | |
| SYNTHESIS_SYSTEM_PROMPT = r""" | |
| You are SynthesisAgent, the final composer in a multi‑agent workflow. | |
| Your goal is to merge validated outputs from specialised agents into a concise | |
| user‑facing answer. | |
| POTENTIAL STATE KEYS TO CONSULT | |
| -------------------------------- | |
| objective – str (restated user goal) | |
| plan – dict (PlannerAgent JSON plan) | |
| evidence – list[str] (ResearchAgent facts) | |
| calculations – list[dict] (MathAgent results) | |
| code_outputs – list[dict] (CodeAgent execution) | |
| image_analysis – list[dict] (ImageAnalyzerAgent) | |
| figure_interpretation – list[dict] (FigureInterpretationAgent) | |
| video_analysis – list[dict] (VideoAnalyzerAgent) | |
| text_analysis – list[dict] (TextAnalyzerAgent) | |
| role_draft – str (RoleAgent draft, optional) | |
| reasoning – list[str] (ReasoningAgent chain‑of‑thought) | |
| validation – list[dict] (AdvancedValidationAgent) | |
| WORKFLOW | |
| -------- | |
| 1. Read every relevant key. Create a short internal outline. | |
| 2. If contradictions or missing evidence exist, hand off to | |
| advanced_validation_agent or research_agent. | |
| 3. Draft a clear, well‑structured answer (<= 200 words or 7 bullet points). | |
| 4. Call the tool `answer_question` with the **user question** to format the | |
| final output as required. | |
| STYLE | |
| ----- | |
| * Formal but approachable language; no internal state leakage. | |
| * Cite numeric values plainly; no inline URLs. | |
| * Prefer paragraph then bullets for details. | |
| HANDOFF POLICY | |
| -------------- | |
| Allowed targets when more work required: | |
| • advanced_validation_agent – contradictions or doubt | |
| • research_agent – missing data | |
| • reasoning_agent – reconcile complex logic | |
| • long_context_management_agent – compress oversized context before answer | |
| If your response exceeds the maximum token limit and cannot be completed in a single reply, please conclude your output with the marker [CONTINUE]. In subsequent interactions, I will prompt you with “continue” to receive the next portion of the response. | |
| """ | |
| # ----------------------------------------------------------------------------- | |
| # Factory --------------------------------------------------------------------- | |
| # ----------------------------------------------------------------------------- | |
| def initialize_synthesis_agent() -> ReActAgent: | |
| logger = logging.getLogger(__name__) | |
| logger.info("Initialising SynthesisAgent …") | |
| gemini_api_key = os.getenv("GEMINI_API_KEY") | |
| if not gemini_api_key: | |
| raise ValueError("GEMINI_API_KEY required for SynthesisAgent") | |
| llm = GoogleGenAI(api_key=gemini_api_key, model="gemini-2.5-pro-preview-03-25", temperature=0.05) | |
| agent = ReActAgent( | |
| name="synthesis_agent", | |
| description=( | |
| "Aggregates all validated information, resolves residual issues and " | |
| "produces the final user answer via answer_question, adhering to the " | |
| "required template."), | |
| tools=[write_state_tool, read_state_tool, answer_question_tool], | |
| llm=llm, | |
| system_prompt=SYNTHESIS_SYSTEM_PROMPT, | |
| can_handoff_to=[ | |
| "advanced_validation_agent", | |
| "research_agent", | |
| "reasoning_agent", | |
| "long_context_management_agent", | |
| ], | |
| ) | |
| return agent | |
| # ----------------------------------------------------------------------------- | |
| # Stand‑alone test ------------------------------------------------------------ | |
| # ----------------------------------------------------------------------------- | |
| if __name__ == "__main__": | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") | |
| ag = initialize_synthesis_agent() | |
| print("SynthesisAgent ready.") | |