application.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. from __future__ import annotations
  2. from dataclasses import dataclass
  3. from pathlib import Path
  4. from cyber_agent.application import (
  5. AgentApplication,
  6. AgentRole,
  7. ApplicationServices,
  8. ProviderRef,
  9. QualityRuleSpec,
  10. ToolSet,
  11. ToolSpec,
  12. )
  13. from cyber_agent.tools import get_tool_registry
  14. from .providers import (
  15. PlaceholderQualityProvider,
  16. ReferenceArtifactStore,
  17. ReferenceCandidateRepository,
  18. ReferenceContextProvider,
  19. ReferenceProjector,
  20. )
  21. from .tools import FactVerifier
  22. _ROLES = Path(__file__).with_name("roles")
  23. def _prompt(role_id: str) -> str:
  24. return (_ROLES / f"{role_id}.prompt").read_text(encoding="utf-8").strip()
  25. @dataclass(frozen=True)
  26. class ReferenceComponents:
  27. application: AgentApplication
  28. services: ApplicationServices
  29. artifacts: ReferenceArtifactStore
  30. candidates: ReferenceCandidateRepository
  31. context: ReferenceContextProvider
  32. facts: FactVerifier
  33. projector: ReferenceProjector
  34. def build_reference_components(
  35. *,
  36. artifacts: ReferenceArtifactStore | None = None,
  37. candidates: ReferenceCandidateRepository | None = None,
  38. projector: ReferenceProjector | None = None,
  39. ) -> ReferenceComponents:
  40. """Build a fresh process-local binding around optionally persistent backends."""
  41. artifacts = artifacts or ReferenceArtifactStore()
  42. candidates = candidates or ReferenceCandidateRepository(artifacts)
  43. context = ReferenceContextProvider()
  44. facts = FactVerifier(artifacts)
  45. projector = projector or ReferenceProjector()
  46. registry = get_tool_registry().clone()
  47. async def verify_fact(
  48. question: str,
  49. context: dict,
  50. uid: str = "",
  51. ) -> dict:
  52. trace = await context["store"].get_trace(context["trace_id"])
  53. if trace is None:
  54. raise ValueError("Fact tool Trace not found")
  55. root_trace_id = trace.context.get("root_trace_id") or trace.trace_id
  56. ref = await facts.verify(
  57. question=question,
  58. root_trace_id=root_trace_id,
  59. uid=uid or None,
  60. )
  61. return {"artifact_ref": ref.model_dump(mode="json")}
  62. registry.register(verify_fact, hidden_params=["context", "uid"])
  63. application = AgentApplication(
  64. application_id="application_reference",
  65. application_version="1",
  66. root_role="editor",
  67. roles=(
  68. AgentRole(
  69. role_id="editor",
  70. model="reference-model",
  71. system_prompt=_prompt("editor"),
  72. tool_sets=("editor-tools",),
  73. allowed_child_roles=("fact_checker", "writer"),
  74. ),
  75. AgentRole(
  76. role_id="fact_checker",
  77. model="reference-model",
  78. system_prompt=_prompt("fact_checker"),
  79. tool_sets=("fact-tools",),
  80. ),
  81. AgentRole(
  82. role_id="writer",
  83. model="reference-model",
  84. system_prompt=_prompt("writer"),
  85. tool_sets=("writer-tools",),
  86. ),
  87. ),
  88. tool_sets=(
  89. ToolSet(
  90. tool_set_id="editor-tools",
  91. tools=tuple(ToolSpec(
  92. tool_name=name,
  93. implementation_version="framework-1",
  94. ) for name in (
  95. "agent",
  96. "update_task_progress",
  97. "review_task_result",
  98. )),
  99. ),
  100. ToolSet(
  101. tool_set_id="fact-tools",
  102. tools=(
  103. ToolSpec(
  104. tool_name="verify_fact",
  105. implementation_version="1",
  106. side_effect_class="read",
  107. ),
  108. ToolSpec(
  109. tool_name="update_task_progress",
  110. implementation_version="framework-1",
  111. ),
  112. ToolSpec(
  113. tool_name="submit_task_report",
  114. implementation_version="framework-1",
  115. ),
  116. ),
  117. ),
  118. ToolSet(
  119. tool_set_id="writer-tools",
  120. tools=tuple(ToolSpec(
  121. tool_name=name,
  122. implementation_version="framework-1",
  123. side_effect_class=("write" if name == "manage_candidate" else "none"),
  124. ) for name in (
  125. "manage_candidate",
  126. "update_task_progress",
  127. "submit_task_report",
  128. )),
  129. ),
  130. ),
  131. artifact_resolver_ref=ProviderRef(
  132. provider_id="reference-artifacts",
  133. provider_version="1",
  134. ),
  135. context_provider_ref=ProviderRef(
  136. provider_id="reference-context",
  137. provider_version="1",
  138. ),
  139. candidate_repository_ref=ProviderRef(
  140. provider_id="reference-candidates",
  141. provider_version="1",
  142. ),
  143. quality_provider_ref=ProviderRef(
  144. provider_id="reference-quality",
  145. provider_version="1",
  146. ),
  147. event_projector_ref=ProviderRef(
  148. provider_id="reference-projector",
  149. provider_version="1",
  150. ),
  151. quality_rules=(QualityRuleSpec(
  152. rule_id="no_placeholder",
  153. version="1",
  154. criterion="Candidate output contains no placeholder description.",
  155. ),),
  156. )
  157. services = ApplicationServices(
  158. tool_registry=registry,
  159. context_provider=context,
  160. artifact_resolver=artifacts,
  161. candidate_repository=candidates,
  162. quality_provider=PlaceholderQualityProvider(),
  163. event_projector=projector,
  164. )
  165. return ReferenceComponents(
  166. application=application,
  167. services=services,
  168. artifacts=artifacts,
  169. candidates=candidates,
  170. context=context,
  171. facts=facts,
  172. projector=projector,
  173. )