agentforge-py 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. agentforge/__init__.py +114 -0
  2. agentforge/_testing/__init__.py +19 -0
  3. agentforge/_testing/fake_llm.py +126 -0
  4. agentforge/_testing/fake_tool.py +122 -0
  5. agentforge/_tools/__init__.py +14 -0
  6. agentforge/_tools/calculator.py +102 -0
  7. agentforge/_tools/decorator.py +300 -0
  8. agentforge/_tools/file_read.py +112 -0
  9. agentforge/_tools/shell.py +134 -0
  10. agentforge/_tools/web_search.py +207 -0
  11. agentforge/agent.py +817 -0
  12. agentforge/auth.py +42 -0
  13. agentforge/cli/__init__.py +18 -0
  14. agentforge/cli/_build.py +323 -0
  15. agentforge/cli/_scaffold_state.py +250 -0
  16. agentforge/cli/_shared_scaffold.py +174 -0
  17. agentforge/cli/config_cmd.py +174 -0
  18. agentforge/cli/db_cmd.py +262 -0
  19. agentforge/cli/debug_cmd.py +168 -0
  20. agentforge/cli/docs_cmd.py +217 -0
  21. agentforge/cli/eval_cmd.py +181 -0
  22. agentforge/cli/health_cmd.py +139 -0
  23. agentforge/cli/list_modules.py +85 -0
  24. agentforge/cli/main.py +81 -0
  25. agentforge/cli/manifest_apply.py +368 -0
  26. agentforge/cli/module_cmd.py +247 -0
  27. agentforge/cli/new_cmd.py +171 -0
  28. agentforge/cli/run_cmd.py +234 -0
  29. agentforge/cli/upgrade_cmd.py +230 -0
  30. agentforge/config/__init__.py +45 -0
  31. agentforge/eval/__init__.py +18 -0
  32. agentforge/eval/consistency.py +107 -0
  33. agentforge/eval/coverage.py +100 -0
  34. agentforge/eval/format_compliance.py +107 -0
  35. agentforge/eval/regression.py +143 -0
  36. agentforge/findings.py +166 -0
  37. agentforge/guardrails/__init__.py +32 -0
  38. agentforge/guardrails/allowlist.py +49 -0
  39. agentforge/guardrails/capability_check.py +58 -0
  40. agentforge/guardrails/engine.py +289 -0
  41. agentforge/guardrails/pii_redact_basic.py +61 -0
  42. agentforge/guardrails/prompt_injection_basic.py +90 -0
  43. agentforge/memory/__init__.py +16 -0
  44. agentforge/memory/in_memory.py +130 -0
  45. agentforge/memory/in_memory_graph.py +262 -0
  46. agentforge/memory/in_memory_vector.py +167 -0
  47. agentforge/pipeline/__init__.py +26 -0
  48. agentforge/pipeline/engine.py +189 -0
  49. agentforge/pipeline/errors.py +19 -0
  50. agentforge/pipeline/tool.py +93 -0
  51. agentforge/py.typed +0 -0
  52. agentforge/recording.py +189 -0
  53. agentforge/renderers/__init__.py +28 -0
  54. agentforge/renderers/_defaults.py +32 -0
  55. agentforge/renderers/markdown.py +44 -0
  56. agentforge/renderers/patch_applier.py +46 -0
  57. agentforge/renderers/registry.py +108 -0
  58. agentforge/renderers/scorecard.py +59 -0
  59. agentforge/renderers/span_table.py +71 -0
  60. agentforge/replay.py +260 -0
  61. agentforge/resolver_register.py +41 -0
  62. agentforge/retrieval.py +410 -0
  63. agentforge/runtime.py +63 -0
  64. agentforge/strategies/__init__.py +27 -0
  65. agentforge/strategies/_base.py +280 -0
  66. agentforge/strategies/_plan.py +93 -0
  67. agentforge/strategies/multi_agent.py +541 -0
  68. agentforge/strategies/plan_execute.py +506 -0
  69. agentforge/strategies/react.py +237 -0
  70. agentforge/strategies/tot.py +472 -0
  71. agentforge/templates/_shared/.cursorrules +12 -0
  72. agentforge/templates/_shared/.github/copilot-instructions.md +13 -0
  73. agentforge/templates/_shared/.gitkeep +0 -0
  74. agentforge/templates/_shared/AGENTS.md.tmpl +123 -0
  75. agentforge/templates/_shared/CLAUDE.md +13 -0
  76. agentforge/templates/_shared/docs/runbooks/01-set-up-new-agent.md.tmpl +67 -0
  77. agentforge/templates/_shared/docs/runbooks/02-add-a-tool.md +67 -0
  78. agentforge/templates/_shared/docs/runbooks/03-add-a-pipeline-task.md +69 -0
  79. agentforge/templates/_shared/docs/runbooks/04-pick-reasoning-strategy.md +67 -0
  80. agentforge/templates/_shared/docs/runbooks/05-write-prompts.md +75 -0
  81. agentforge/templates/_shared/docs/runbooks/06-test-your-agent.md +75 -0
  82. agentforge/templates/_shared/docs/runbooks/07-debug-a-run.md +70 -0
  83. agentforge/templates/_shared/docs/runbooks/08-add-memory.md +75 -0
  84. agentforge/templates/_shared/docs/runbooks/09-add-mcp.md +78 -0
  85. agentforge/templates/_shared/docs/runbooks/10-add-evaluators.md +76 -0
  86. agentforge/templates/_shared/docs/runbooks/11-add-safety-guardrails.md +83 -0
  87. agentforge/templates/_shared/docs/runbooks/12-add-observability.md +77 -0
  88. agentforge/templates/_shared/docs/runbooks/13-configure-multi-provider.md +91 -0
  89. agentforge/templates/_shared/docs/runbooks/14-deploy-your-agent.md +70 -0
  90. agentforge/templates/_shared/docs/runbooks/15-upgrade-your-agent.md +67 -0
  91. agentforge/templates/_shared/docs/runbooks/16-configuration-reference.md +81 -0
  92. agentforge/templates/_shared/docs/runbooks/17-add-reranker.md +78 -0
  93. agentforge/templates/_shared/docs/runbooks/18-add-hybrid-search.md +78 -0
  94. agentforge/templates/_shared/docs/runbooks/19-add-graphrag.md +83 -0
  95. agentforge/templates/_shared/docs/runbooks/20-apply-schema-migrations.md +92 -0
  96. agentforge/templates/_shared/docs/runbooks/21-use-streaming-guardrails.md +82 -0
  97. agentforge/templates/_shared/docs/runbooks/README.md.tmpl +68 -0
  98. agentforge/templates/code-reviewer/.env.example +8 -0
  99. agentforge/templates/code-reviewer/.gitignore +7 -0
  100. agentforge/templates/code-reviewer/README.md +12 -0
  101. agentforge/templates/code-reviewer/agentforge.yaml +23 -0
  102. agentforge/templates/code-reviewer/copier.yml +34 -0
  103. agentforge/templates/code-reviewer/pyproject.toml +18 -0
  104. agentforge/templates/code-reviewer/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
  105. agentforge/templates/code-reviewer/src/{{project_slug.replace('-', '_')}}/main.py +32 -0
  106. agentforge/templates/docs-qa/.env.example +8 -0
  107. agentforge/templates/docs-qa/.gitignore +7 -0
  108. agentforge/templates/docs-qa/README.md +14 -0
  109. agentforge/templates/docs-qa/agentforge.yaml +19 -0
  110. agentforge/templates/docs-qa/copier.yml +31 -0
  111. agentforge/templates/docs-qa/pyproject.toml +18 -0
  112. agentforge/templates/docs-qa/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
  113. agentforge/templates/docs-qa/src/{{project_slug.replace('-', '_')}}/main.py +32 -0
  114. agentforge/templates/minimal/.env.example +11 -0
  115. agentforge/templates/minimal/.gitignore +10 -0
  116. agentforge/templates/minimal/README.md +28 -0
  117. agentforge/templates/minimal/agentforge.yaml +10 -0
  118. agentforge/templates/minimal/copier.yml +52 -0
  119. agentforge/templates/minimal/pyproject.toml +18 -0
  120. agentforge/templates/minimal/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
  121. agentforge/templates/minimal/src/{{project_slug.replace('-', '_')}}/main.py +34 -0
  122. agentforge/templates/patch-bot/.env.example +8 -0
  123. agentforge/templates/patch-bot/.gitignore +7 -0
  124. agentforge/templates/patch-bot/README.md +13 -0
  125. agentforge/templates/patch-bot/agentforge.yaml +15 -0
  126. agentforge/templates/patch-bot/copier.yml +31 -0
  127. agentforge/templates/patch-bot/pyproject.toml +18 -0
  128. agentforge/templates/patch-bot/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
  129. agentforge/templates/patch-bot/src/{{project_slug.replace('-', '_')}}/main.py +32 -0
  130. agentforge/templates/research/.env.example +8 -0
  131. agentforge/templates/research/.gitignore +7 -0
  132. agentforge/templates/research/README.md +14 -0
  133. agentforge/templates/research/agentforge.yaml +17 -0
  134. agentforge/templates/research/copier.yml +31 -0
  135. agentforge/templates/research/pyproject.toml +18 -0
  136. agentforge/templates/research/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
  137. agentforge/templates/research/src/{{project_slug.replace('-', '_')}}/main.py +31 -0
  138. agentforge/templates/triage/.env.example +8 -0
  139. agentforge/templates/triage/.gitignore +7 -0
  140. agentforge/templates/triage/README.md +14 -0
  141. agentforge/templates/triage/agentforge.yaml +25 -0
  142. agentforge/templates/triage/copier.yml +31 -0
  143. agentforge/templates/triage/pyproject.toml +18 -0
  144. agentforge/templates/triage/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
  145. agentforge/templates/triage/src/{{project_slug.replace('-', '_')}}/main.py +30 -0
  146. agentforge/testing/__init__.py +69 -0
  147. agentforge/testing/conformance.py +40 -0
  148. agentforge/testing/factory.py +89 -0
  149. agentforge/testing/fixtures.py +42 -0
  150. agentforge/testing/llm.py +235 -0
  151. agentforge/testing/recording.py +177 -0
  152. agentforge/tools/__init__.py +41 -0
  153. agentforge_py-0.2.1.dist-info/METADATA +158 -0
  154. agentforge_py-0.2.1.dist-info/RECORD +157 -0
  155. agentforge_py-0.2.1.dist-info/WHEEL +4 -0
  156. agentforge_py-0.2.1.dist-info/entry_points.txt +2 -0
  157. agentforge_py-0.2.1.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,32 @@
1
+ """Entry point for {{ project_name }}.
2
+
3
+ Docs / source Q&A. Wires `file_read` + `web_search` so the agent
4
+ can pull both local docs and external context. Outputs
5
+ `NarrativeFinding`s (feat-008).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import sys
12
+
13
+ from agentforge import Agent
14
+ from agentforge.tools import file_read, web_search
15
+
16
+
17
+ async def run_agent(task: str) -> str:
18
+ async with Agent(tools=[file_read, web_search]) as agent:
19
+ result = await agent.run(task)
20
+ return str(result.output)
21
+
22
+
23
+ def main() -> None:
24
+ if len(sys.argv) < 2:
25
+ print('Usage: python -m {{ project_slug | replace("-", "_") }} "<question>"')
26
+ sys.exit(1)
27
+ output = asyncio.run(run_agent(" ".join(sys.argv[1:])))
28
+ print(output)
29
+
30
+
31
+ if __name__ == "__main__":
32
+ main()
@@ -0,0 +1,11 @@
1
+ # Environment variables for {{ project_name }}.
2
+ # Copy this file to `.env` and fill in real values.
3
+
4
+ {% if llm_provider == 'bedrock' -%}
5
+ # AWS Bedrock credentials (or use AWS_PROFILE / instance role).
6
+ AWS_REGION=us-east-1
7
+ {% elif llm_provider == 'anthropic' -%}
8
+ ANTHROPIC_API_KEY=
9
+ {% else -%}
10
+ OPENAI_API_KEY=
11
+ {% endif %}
@@ -0,0 +1,10 @@
1
+ .env
2
+ .venv/
3
+ __pycache__/
4
+ *.pyc
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .mypy_cache/
8
+
9
+ # Keep .agentforge-state checked in — it tracks framework managed
10
+ # files for upgrade.
@@ -0,0 +1,28 @@
1
+ # {{ project_name }}
2
+
3
+ {{ description }}
4
+
5
+ ## Getting started
6
+
7
+ ```bash
8
+ uv sync
9
+ cp .env.example .env
10
+ # Fill in credentials in .env, then:
11
+ python -m {{ project_slug | replace('-', '_') }} "your task here"
12
+ ```
13
+
14
+ ## Configuration
15
+
16
+ `agentforge.yaml` is the agent's wiring — model, budget, modules.
17
+ See `agentforge config show` for the resolved config and
18
+ `agentforge config validate` to check it.
19
+
20
+ ## Upgrades
21
+
22
+ ```bash
23
+ agentforge upgrade
24
+ ```
25
+
26
+ Pulls in framework updates while preserving your customisations.
27
+ See `agentforge status` for what's managed-by-the-framework vs
28
+ forked-by-you.
@@ -0,0 +1,10 @@
1
+ agent:
2
+ name: "{{ project_slug }}"
3
+ model: "{% if llm_provider == 'bedrock' %}bedrock:us.anthropic.claude-sonnet-4-5-20250929{% elif llm_provider == 'anthropic' %}anthropic:claude-sonnet-4-5{% else %}openai:gpt-4o{% endif %}"
4
+ budget:
5
+ usd: 2.0
6
+ max_iterations: 25
7
+
8
+ logging:
9
+ level: "INFO"
10
+ format: "text"
@@ -0,0 +1,52 @@
1
+ # Copier template: minimal AgentForge agent (feat-011).
2
+ #
3
+ # Simplest working starter: one Agent, one tool, one test. No
4
+ # persistence, no evaluators, no observability. Good for proofs of
5
+ # concept and for learning what a working agent looks like.
6
+
7
+ _min_copier_version: "9.4.0"
8
+
9
+ # Render the answer file so `agentforge upgrade` knows what to ask.
10
+ _answers_file: ".agentforge-state/answers.yml"
11
+
12
+ # Render every file's content as a Jinja template (Cookiecutter-
13
+ # style). Default (templates_suffix=".jinja") would force every
14
+ # file to be named `foo.py.jinja`, which is ugly.
15
+ _templates_suffix: ""
16
+
17
+ # No `_subdirectory` — template files live at the root and render
18
+ # directly into the destination directory. The CLI passes
19
+ # `dst_path = ./<project_slug>` so the rendered output has a clean
20
+ # project root.
21
+
22
+ project_name:
23
+ type: str
24
+ help: "Human-readable project name (e.g. 'My PR Reviewer')."
25
+ default: "My Agent"
26
+
27
+ project_slug:
28
+ type: str
29
+ help: "Python package + directory name (kebab-case-ok, becomes snake_case in Python imports)."
30
+ default: "my-agent"
31
+ validator: >-
32
+ {% if not (project_slug | regex_search('^[a-z][a-z0-9-]*$')) %}
33
+ Must be lowercase alphanumeric + dashes, starting with a letter.
34
+ {% endif %}
35
+
36
+ llm_provider:
37
+ type: str
38
+ help: "Which LLM provider to use?"
39
+ choices:
40
+ Bedrock (AWS): bedrock
41
+ Anthropic (direct): anthropic
42
+ OpenAI: openai
43
+ default: bedrock
44
+
45
+ description:
46
+ type: str
47
+ help: "One-line description of the agent."
48
+ default: "An AgentForge agent."
49
+
50
+ # Template metadata pulled into the lock-file marker header by the
51
+ # `agentforge new` CLI after Copier finishes rendering.
52
+ _template_name: "minimal"
@@ -0,0 +1,18 @@
1
+ [project]
2
+ name = "{{ project_slug }}"
3
+ version = "0.0.0"
4
+ description = "{{ description }}"
5
+ requires-python = ">=3.13"
6
+ dependencies = [
7
+ "agentforge-py",
8
+ {%- if llm_provider == 'bedrock' %}
9
+ "agentforge-bedrock",
10
+ {%- endif %}
11
+ ]
12
+
13
+ [build-system]
14
+ requires = ["hatchling>=1.27"]
15
+ build-backend = "hatchling.build"
16
+
17
+ [tool.hatch.build.targets.wheel]
18
+ packages = ["src/{{ project_slug | replace('-', '_') }}"]
@@ -0,0 +1,5 @@
1
+ """{{ project_name }}: {{ description }}"""
2
+
3
+ from {{ project_slug | replace('-', '_') }}.main import run_agent
4
+
5
+ __all__ = ["run_agent"]
@@ -0,0 +1,34 @@
1
+ """Entry point for {{ project_name }}.
2
+
3
+ Edit this file to add tools, prompts, evaluators, etc. The
4
+ `Agent` constructor surface is feat-001's locked Public API — see
5
+ `docs/features/feat-001-core-contracts-and-agent.md` in the
6
+ agentforge-py repo for every kwarg.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import asyncio
12
+ import sys
13
+
14
+ from agentforge import Agent
15
+
16
+
17
+ async def run_agent(task: str) -> str:
18
+ """Run the agent against `task` and return its output."""
19
+ async with Agent() as agent:
20
+ result = await agent.run(task)
21
+ return str(result.output)
22
+
23
+
24
+ def main() -> None:
25
+ if len(sys.argv) < 2:
26
+ print('Usage: python -m {{ project_slug | replace("-", "_") }} "<task>"')
27
+ sys.exit(1)
28
+ task = " ".join(sys.argv[1:])
29
+ output = asyncio.run(run_agent(task))
30
+ print(output)
31
+
32
+
33
+ if __name__ == "__main__":
34
+ main()
@@ -0,0 +1,8 @@
1
+ # Credentials for {{ project_name }}.
2
+ {% if llm_provider == 'bedrock' -%}
3
+ AWS_REGION=us-east-1
4
+ {% elif llm_provider == 'anthropic' -%}
5
+ ANTHROPIC_API_KEY=
6
+ {% else -%}
7
+ OPENAI_API_KEY=
8
+ {% endif %}
@@ -0,0 +1,7 @@
1
+ .env
2
+ .venv/
3
+ __pycache__/
4
+ *.pyc
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .mypy_cache/
@@ -0,0 +1,13 @@
1
+ # {{ project_name }}
2
+
3
+ {{ description }} Emits `PatchFinding`s — structured unified diffs
4
+ with rationale + confidence — for downstream consumers to apply.
5
+
6
+ ```bash
7
+ uv sync
8
+ cp .env.example .env
9
+ python -m {{ project_slug | replace('-', '_') }} "replace deprecated time.clock() in src/"
10
+ ```
11
+
12
+ The agent does NOT apply the patches itself — the consumer (CI
13
+ bot, codemod script, etc.) reads `result.findings` and decides.
@@ -0,0 +1,15 @@
1
+ agent:
2
+ name: "{{ project_slug }}"
3
+ model: "{% if llm_provider == 'bedrock' %}bedrock:us.anthropic.claude-sonnet-4-5-20250929{% elif llm_provider == 'anthropic' %}anthropic:claude-sonnet-4-5{% else %}openai:gpt-4o{% endif %}"
4
+ system_prompt: |
5
+ You generate patches. For each fix, emit a `PatchFinding` with
6
+ a unified-diff `patch`, a one-line `rationale`, and a
7
+ `confidence` in [0, 1]. Only return patches you can defend with
8
+ evidence from the source.
9
+ budget:
10
+ usd: 5.0
11
+ max_iterations: 40
12
+
13
+ logging:
14
+ level: "INFO"
15
+ format: "text"
@@ -0,0 +1,31 @@
1
+ # Copier template: patch-bot agent (feat-011).
2
+ #
3
+ # Emits PatchFinding — structured diffs with rationale and
4
+ # confidence. Use for codemod / auto-fix workflows where the
5
+ # downstream consumer applies the patch.
6
+
7
+ _min_copier_version: "9.4.0"
8
+ _answers_file: ".agentforge-state/answers.yml"
9
+ _templates_suffix: ""
10
+
11
+ project_name:
12
+ type: str
13
+ default: "Patch Bot"
14
+
15
+ project_slug:
16
+ type: str
17
+ default: "patch-bot"
18
+
19
+ llm_provider:
20
+ type: str
21
+ choices:
22
+ Bedrock (AWS): bedrock
23
+ Anthropic (direct): anthropic
24
+ OpenAI: openai
25
+ default: bedrock
26
+
27
+ description:
28
+ type: str
29
+ default: "A patch-emitting AgentForge agent."
30
+
31
+ _template_name: "patch-bot"
@@ -0,0 +1,18 @@
1
+ [project]
2
+ name = "{{ project_slug }}"
3
+ version = "0.0.0"
4
+ description = "{{ description }}"
5
+ requires-python = ">=3.13"
6
+ dependencies = [
7
+ "agentforge-py",
8
+ {%- if llm_provider == 'bedrock' %}
9
+ "agentforge-bedrock",
10
+ {%- endif %}
11
+ ]
12
+
13
+ [build-system]
14
+ requires = ["hatchling>=1.27"]
15
+ build-backend = "hatchling.build"
16
+
17
+ [tool.hatch.build.targets.wheel]
18
+ packages = ["src/{{ project_slug | replace('-', '_') }}"]
@@ -0,0 +1,5 @@
1
+ """{{ project_name }}: {{ description }}"""
2
+
3
+ from {{ project_slug | replace('-', '_') }}.main import run_agent
4
+
5
+ __all__ = ["run_agent"]
@@ -0,0 +1,32 @@
1
+ """Entry point for {{ project_name }}.
2
+
3
+ Emits PatchFinding (feat-008) — structured unified diffs with
4
+ rationale + confidence. Tools include `file_read` so the bot can
5
+ pull source context before proposing a patch.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import sys
12
+
13
+ from agentforge import Agent
14
+ from agentforge.tools import file_read
15
+
16
+
17
+ async def run_agent(task: str) -> str:
18
+ async with Agent(tools=[file_read]) as agent:
19
+ result = await agent.run(task)
20
+ return str(result.output)
21
+
22
+
23
+ def main() -> None:
24
+ if len(sys.argv) < 2:
25
+ print('Usage: python -m {{ project_slug | replace("-", "_") }} "<task>"')
26
+ sys.exit(1)
27
+ output = asyncio.run(run_agent(" ".join(sys.argv[1:])))
28
+ print(output)
29
+
30
+
31
+ if __name__ == "__main__":
32
+ main()
@@ -0,0 +1,8 @@
1
+ # Credentials for {{ project_name }}.
2
+ {% if llm_provider == 'bedrock' -%}
3
+ AWS_REGION=us-east-1
4
+ {% elif llm_provider == 'anthropic' -%}
5
+ ANTHROPIC_API_KEY=
6
+ {% else -%}
7
+ OPENAI_API_KEY=
8
+ {% endif %}
@@ -0,0 +1,7 @@
1
+ .env
2
+ .venv/
3
+ __pycache__/
4
+ *.pyc
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .mypy_cache/
@@ -0,0 +1,14 @@
1
+ # {{ project_name }}
2
+
3
+ {{ description }} Open-ended research with citations. Uses the
4
+ Plan-Execute strategy + web_search to break a complex question
5
+ into sub-tasks, then synthesises a `NarrativeFinding`.
6
+
7
+ ```bash
8
+ uv sync
9
+ cp .env.example .env
10
+ python -m {{ project_slug | replace('-', '_') }} "what changed in claude-sonnet 4.5 vs 4.7?"
11
+ ```
12
+
13
+ Defaults to `web_search` (DuckDuckGo HTML scrape). Replace with a
14
+ serper / tavily backend by passing `search_fn=` to `WebSearchTool`.
@@ -0,0 +1,17 @@
1
+ agent:
2
+ name: "{{ project_slug }}"
3
+ model: "{% if llm_provider == 'bedrock' %}bedrock:us.anthropic.claude-sonnet-4-5-20250929{% elif llm_provider == 'anthropic' %}anthropic:claude-sonnet-4-5{% else %}openai:gpt-4o{% endif %}"
4
+ strategy: "plan-execute"
5
+ system_prompt: |
6
+ You answer open-ended research questions. Plan the sub-tasks,
7
+ execute them (use web_search liberally), then synthesise into a
8
+ NarrativeFinding: `body` is the markdown answer; `references`
9
+ is every URL you actually used. Don't cite sources you didn't
10
+ open.
11
+ budget:
12
+ usd: 5.0
13
+ max_iterations: 50
14
+
15
+ logging:
16
+ level: "INFO"
17
+ format: "text"
@@ -0,0 +1,31 @@
1
+ # Copier template: research agent (feat-011).
2
+ #
3
+ # Long-form research with citations. Uses Plan-Execute strategy for
4
+ # multi-step questions, web_search for sources, NarrativeFinding for
5
+ # the output shape.
6
+
7
+ _min_copier_version: "9.4.0"
8
+ _answers_file: ".agentforge-state/answers.yml"
9
+ _templates_suffix: ""
10
+
11
+ project_name:
12
+ type: str
13
+ default: "Research Agent"
14
+
15
+ project_slug:
16
+ type: str
17
+ default: "research-agent"
18
+
19
+ llm_provider:
20
+ type: str
21
+ choices:
22
+ Bedrock (AWS): bedrock
23
+ Anthropic (direct): anthropic
24
+ OpenAI: openai
25
+ default: bedrock
26
+
27
+ description:
28
+ type: str
29
+ default: "A research AgentForge agent."
30
+
31
+ _template_name: "research"
@@ -0,0 +1,18 @@
1
+ [project]
2
+ name = "{{ project_slug }}"
3
+ version = "0.0.0"
4
+ description = "{{ description }}"
5
+ requires-python = ">=3.13"
6
+ dependencies = [
7
+ "agentforge-py",
8
+ {%- if llm_provider == 'bedrock' %}
9
+ "agentforge-bedrock",
10
+ {%- endif %}
11
+ ]
12
+
13
+ [build-system]
14
+ requires = ["hatchling>=1.27"]
15
+ build-backend = "hatchling.build"
16
+
17
+ [tool.hatch.build.targets.wheel]
18
+ packages = ["src/{{ project_slug | replace('-', '_') }}"]
@@ -0,0 +1,5 @@
1
+ """{{ project_name }}: {{ description }}"""
2
+
3
+ from {{ project_slug | replace('-', '_') }}.main import run_agent
4
+
5
+ __all__ = ["run_agent"]
@@ -0,0 +1,31 @@
1
+ """Entry point for {{ project_name }}.
2
+
3
+ Research with citations. Plan-Execute strategy + web_search.
4
+ Outputs NarrativeFinding (feat-008).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ import sys
11
+
12
+ from agentforge import Agent
13
+ from agentforge.tools import web_search
14
+
15
+
16
+ async def run_agent(question: str) -> str:
17
+ async with Agent(tools=[web_search]) as agent:
18
+ result = await agent.run(question)
19
+ return str(result.output)
20
+
21
+
22
+ def main() -> None:
23
+ if len(sys.argv) < 2:
24
+ print('Usage: python -m {{ project_slug | replace("-", "_") }} "<research question>"')
25
+ sys.exit(1)
26
+ output = asyncio.run(run_agent(" ".join(sys.argv[1:])))
27
+ print(output)
28
+
29
+
30
+ if __name__ == "__main__":
31
+ main()
@@ -0,0 +1,8 @@
1
+ # Credentials for {{ project_name }}.
2
+ {% if llm_provider == 'bedrock' -%}
3
+ AWS_REGION=us-east-1
4
+ {% elif llm_provider == 'anthropic' -%}
5
+ ANTHROPIC_API_KEY=
6
+ {% else -%}
7
+ OPENAI_API_KEY=
8
+ {% endif %}
@@ -0,0 +1,7 @@
1
+ .env
2
+ .venv/
3
+ __pycache__/
4
+ *.pyc
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .mypy_cache/
@@ -0,0 +1,14 @@
1
+ # {{ project_name }}
2
+
3
+ {{ description }} Classifies incoming items by severity + category.
4
+ Uses Haiku-tier models — triage is high-volume; the smaller model
5
+ keeps cost bounded.
6
+
7
+ ```bash
8
+ uv sync
9
+ cp .env.example .env
10
+ python -m {{ project_slug | replace('-', '_') }} "triage this issue text..."
11
+ ```
12
+
13
+ Add a `Coverage` evaluator with a known-issue reference set to
14
+ score how completely the agent classifies a batch.
@@ -0,0 +1,25 @@
1
+ agent:
2
+ name: "{{ project_slug }}"
3
+ model: "{% if llm_provider == 'bedrock' %}bedrock:us.anthropic.claude-haiku-4-5{% elif llm_provider == 'anthropic' %}anthropic:claude-haiku-4-5{% else %}openai:gpt-4o-mini{% endif %}"
4
+ system_prompt: |
5
+ You triage incoming issues / tickets / alerts. For each item
6
+ emit a `SimpleFinding` with `severity` in
7
+ {critical, warning, suggestion, info}, a one-word `category`,
8
+ a one-line `message`, and an actionable `recommendation`.
9
+ Classify, don't speculate.
10
+ budget:
11
+ usd: 1.0
12
+ max_iterations: 15
13
+
14
+ # Pair with a Coverage evaluator once you have a known-issue
15
+ # reference set:
16
+ #
17
+ # modules:
18
+ # evaluators:
19
+ # - name: coverage
20
+ # config:
21
+ # reference: ["security-issue-1", "perf-issue-2", ...]
22
+
23
+ logging:
24
+ level: "INFO"
25
+ format: "text"
@@ -0,0 +1,31 @@
1
+ # Copier template: triage agent (feat-011).
2
+ #
3
+ # Classifies incoming issues / tickets / alerts by severity +
4
+ # category. Emits SimpleFinding per item; pairs well with a
5
+ # Coverage evaluator against a known-issue set.
6
+
7
+ _min_copier_version: "9.4.0"
8
+ _answers_file: ".agentforge-state/answers.yml"
9
+ _templates_suffix: ""
10
+
11
+ project_name:
12
+ type: str
13
+ default: "Triage"
14
+
15
+ project_slug:
16
+ type: str
17
+ default: "triage"
18
+
19
+ llm_provider:
20
+ type: str
21
+ choices:
22
+ Bedrock (AWS): bedrock
23
+ Anthropic (direct): anthropic
24
+ OpenAI: openai
25
+ default: bedrock
26
+
27
+ description:
28
+ type: str
29
+ default: "A triage AgentForge agent."
30
+
31
+ _template_name: "triage"
@@ -0,0 +1,18 @@
1
+ [project]
2
+ name = "{{ project_slug }}"
3
+ version = "0.0.0"
4
+ description = "{{ description }}"
5
+ requires-python = ">=3.13"
6
+ dependencies = [
7
+ "agentforge-py",
8
+ {%- if llm_provider == 'bedrock' %}
9
+ "agentforge-bedrock",
10
+ {%- endif %}
11
+ ]
12
+
13
+ [build-system]
14
+ requires = ["hatchling>=1.27"]
15
+ build-backend = "hatchling.build"
16
+
17
+ [tool.hatch.build.targets.wheel]
18
+ packages = ["src/{{ project_slug | replace('-', '_') }}"]
@@ -0,0 +1,5 @@
1
+ """{{ project_name }}: {{ description }}"""
2
+
3
+ from {{ project_slug | replace('-', '_') }}.main import run_agent
4
+
5
+ __all__ = ["run_agent"]
@@ -0,0 +1,30 @@
1
+ """Entry point for {{ project_name }}.
2
+
3
+ Triage: classify incoming items into severity + category. No
4
+ external tools by default — the agent works from the text alone.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ import sys
11
+
12
+ from agentforge import Agent
13
+
14
+
15
+ async def run_agent(item: str) -> str:
16
+ async with Agent() as agent:
17
+ result = await agent.run(item)
18
+ return str(result.output)
19
+
20
+
21
+ def main() -> None:
22
+ if len(sys.argv) < 2:
23
+ print('Usage: python -m {{ project_slug | replace("-", "_") }} "<item to triage>"')
24
+ sys.exit(1)
25
+ output = asyncio.run(run_agent(" ".join(sys.argv[1:])))
26
+ print(output)
27
+
28
+
29
+ if __name__ == "__main__":
30
+ main()