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.
- agentforge/__init__.py +114 -0
- agentforge/_testing/__init__.py +19 -0
- agentforge/_testing/fake_llm.py +126 -0
- agentforge/_testing/fake_tool.py +122 -0
- agentforge/_tools/__init__.py +14 -0
- agentforge/_tools/calculator.py +102 -0
- agentforge/_tools/decorator.py +300 -0
- agentforge/_tools/file_read.py +112 -0
- agentforge/_tools/shell.py +134 -0
- agentforge/_tools/web_search.py +207 -0
- agentforge/agent.py +817 -0
- agentforge/auth.py +42 -0
- agentforge/cli/__init__.py +18 -0
- agentforge/cli/_build.py +323 -0
- agentforge/cli/_scaffold_state.py +250 -0
- agentforge/cli/_shared_scaffold.py +174 -0
- agentforge/cli/config_cmd.py +174 -0
- agentforge/cli/db_cmd.py +262 -0
- agentforge/cli/debug_cmd.py +168 -0
- agentforge/cli/docs_cmd.py +217 -0
- agentforge/cli/eval_cmd.py +181 -0
- agentforge/cli/health_cmd.py +139 -0
- agentforge/cli/list_modules.py +85 -0
- agentforge/cli/main.py +81 -0
- agentforge/cli/manifest_apply.py +368 -0
- agentforge/cli/module_cmd.py +247 -0
- agentforge/cli/new_cmd.py +171 -0
- agentforge/cli/run_cmd.py +234 -0
- agentforge/cli/upgrade_cmd.py +230 -0
- agentforge/config/__init__.py +45 -0
- agentforge/eval/__init__.py +18 -0
- agentforge/eval/consistency.py +107 -0
- agentforge/eval/coverage.py +100 -0
- agentforge/eval/format_compliance.py +107 -0
- agentforge/eval/regression.py +143 -0
- agentforge/findings.py +166 -0
- agentforge/guardrails/__init__.py +32 -0
- agentforge/guardrails/allowlist.py +49 -0
- agentforge/guardrails/capability_check.py +58 -0
- agentforge/guardrails/engine.py +289 -0
- agentforge/guardrails/pii_redact_basic.py +61 -0
- agentforge/guardrails/prompt_injection_basic.py +90 -0
- agentforge/memory/__init__.py +16 -0
- agentforge/memory/in_memory.py +130 -0
- agentforge/memory/in_memory_graph.py +262 -0
- agentforge/memory/in_memory_vector.py +167 -0
- agentforge/pipeline/__init__.py +26 -0
- agentforge/pipeline/engine.py +189 -0
- agentforge/pipeline/errors.py +19 -0
- agentforge/pipeline/tool.py +93 -0
- agentforge/py.typed +0 -0
- agentforge/recording.py +189 -0
- agentforge/renderers/__init__.py +28 -0
- agentforge/renderers/_defaults.py +32 -0
- agentforge/renderers/markdown.py +44 -0
- agentforge/renderers/patch_applier.py +46 -0
- agentforge/renderers/registry.py +108 -0
- agentforge/renderers/scorecard.py +59 -0
- agentforge/renderers/span_table.py +71 -0
- agentforge/replay.py +260 -0
- agentforge/resolver_register.py +41 -0
- agentforge/retrieval.py +410 -0
- agentforge/runtime.py +63 -0
- agentforge/strategies/__init__.py +27 -0
- agentforge/strategies/_base.py +280 -0
- agentforge/strategies/_plan.py +93 -0
- agentforge/strategies/multi_agent.py +541 -0
- agentforge/strategies/plan_execute.py +506 -0
- agentforge/strategies/react.py +237 -0
- agentforge/strategies/tot.py +472 -0
- agentforge/templates/_shared/.cursorrules +12 -0
- agentforge/templates/_shared/.github/copilot-instructions.md +13 -0
- agentforge/templates/_shared/.gitkeep +0 -0
- agentforge/templates/_shared/AGENTS.md.tmpl +123 -0
- agentforge/templates/_shared/CLAUDE.md +13 -0
- agentforge/templates/_shared/docs/runbooks/01-set-up-new-agent.md.tmpl +67 -0
- agentforge/templates/_shared/docs/runbooks/02-add-a-tool.md +67 -0
- agentforge/templates/_shared/docs/runbooks/03-add-a-pipeline-task.md +69 -0
- agentforge/templates/_shared/docs/runbooks/04-pick-reasoning-strategy.md +67 -0
- agentforge/templates/_shared/docs/runbooks/05-write-prompts.md +75 -0
- agentforge/templates/_shared/docs/runbooks/06-test-your-agent.md +75 -0
- agentforge/templates/_shared/docs/runbooks/07-debug-a-run.md +70 -0
- agentforge/templates/_shared/docs/runbooks/08-add-memory.md +75 -0
- agentforge/templates/_shared/docs/runbooks/09-add-mcp.md +78 -0
- agentforge/templates/_shared/docs/runbooks/10-add-evaluators.md +76 -0
- agentforge/templates/_shared/docs/runbooks/11-add-safety-guardrails.md +83 -0
- agentforge/templates/_shared/docs/runbooks/12-add-observability.md +77 -0
- agentforge/templates/_shared/docs/runbooks/13-configure-multi-provider.md +91 -0
- agentforge/templates/_shared/docs/runbooks/14-deploy-your-agent.md +70 -0
- agentforge/templates/_shared/docs/runbooks/15-upgrade-your-agent.md +67 -0
- agentforge/templates/_shared/docs/runbooks/16-configuration-reference.md +81 -0
- agentforge/templates/_shared/docs/runbooks/17-add-reranker.md +78 -0
- agentforge/templates/_shared/docs/runbooks/18-add-hybrid-search.md +78 -0
- agentforge/templates/_shared/docs/runbooks/19-add-graphrag.md +83 -0
- agentforge/templates/_shared/docs/runbooks/20-apply-schema-migrations.md +92 -0
- agentforge/templates/_shared/docs/runbooks/21-use-streaming-guardrails.md +82 -0
- agentforge/templates/_shared/docs/runbooks/README.md.tmpl +68 -0
- agentforge/templates/code-reviewer/.env.example +8 -0
- agentforge/templates/code-reviewer/.gitignore +7 -0
- agentforge/templates/code-reviewer/README.md +12 -0
- agentforge/templates/code-reviewer/agentforge.yaml +23 -0
- agentforge/templates/code-reviewer/copier.yml +34 -0
- agentforge/templates/code-reviewer/pyproject.toml +18 -0
- agentforge/templates/code-reviewer/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
- agentforge/templates/code-reviewer/src/{{project_slug.replace('-', '_')}}/main.py +32 -0
- agentforge/templates/docs-qa/.env.example +8 -0
- agentforge/templates/docs-qa/.gitignore +7 -0
- agentforge/templates/docs-qa/README.md +14 -0
- agentforge/templates/docs-qa/agentforge.yaml +19 -0
- agentforge/templates/docs-qa/copier.yml +31 -0
- agentforge/templates/docs-qa/pyproject.toml +18 -0
- agentforge/templates/docs-qa/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
- agentforge/templates/docs-qa/src/{{project_slug.replace('-', '_')}}/main.py +32 -0
- agentforge/templates/minimal/.env.example +11 -0
- agentforge/templates/minimal/.gitignore +10 -0
- agentforge/templates/minimal/README.md +28 -0
- agentforge/templates/minimal/agentforge.yaml +10 -0
- agentforge/templates/minimal/copier.yml +52 -0
- agentforge/templates/minimal/pyproject.toml +18 -0
- agentforge/templates/minimal/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
- agentforge/templates/minimal/src/{{project_slug.replace('-', '_')}}/main.py +34 -0
- agentforge/templates/patch-bot/.env.example +8 -0
- agentforge/templates/patch-bot/.gitignore +7 -0
- agentforge/templates/patch-bot/README.md +13 -0
- agentforge/templates/patch-bot/agentforge.yaml +15 -0
- agentforge/templates/patch-bot/copier.yml +31 -0
- agentforge/templates/patch-bot/pyproject.toml +18 -0
- agentforge/templates/patch-bot/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
- agentforge/templates/patch-bot/src/{{project_slug.replace('-', '_')}}/main.py +32 -0
- agentforge/templates/research/.env.example +8 -0
- agentforge/templates/research/.gitignore +7 -0
- agentforge/templates/research/README.md +14 -0
- agentforge/templates/research/agentforge.yaml +17 -0
- agentforge/templates/research/copier.yml +31 -0
- agentforge/templates/research/pyproject.toml +18 -0
- agentforge/templates/research/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
- agentforge/templates/research/src/{{project_slug.replace('-', '_')}}/main.py +31 -0
- agentforge/templates/triage/.env.example +8 -0
- agentforge/templates/triage/.gitignore +7 -0
- agentforge/templates/triage/README.md +14 -0
- agentforge/templates/triage/agentforge.yaml +25 -0
- agentforge/templates/triage/copier.yml +31 -0
- agentforge/templates/triage/pyproject.toml +18 -0
- agentforge/templates/triage/src/{{project_slug.replace('-', '_')}}/__init__.py +5 -0
- agentforge/templates/triage/src/{{project_slug.replace('-', '_')}}/main.py +30 -0
- agentforge/testing/__init__.py +69 -0
- agentforge/testing/conformance.py +40 -0
- agentforge/testing/factory.py +89 -0
- agentforge/testing/fixtures.py +42 -0
- agentforge/testing/llm.py +235 -0
- agentforge/testing/recording.py +177 -0
- agentforge/tools/__init__.py +41 -0
- agentforge_py-0.2.1.dist-info/METADATA +158 -0
- agentforge_py-0.2.1.dist-info/RECORD +157 -0
- agentforge_py-0.2.1.dist-info/WHEEL +4 -0
- agentforge_py-0.2.1.dist-info/entry_points.txt +2 -0
- agentforge_py-0.2.1.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# 18 — Add hybrid search (BM25 + vector)
|
|
2
|
+
|
|
3
|
+
> **Goal:** combine keyword (BM25) and semantic (vector) recall
|
|
4
|
+
> so queries that hit a specific term don't get out-voted by
|
|
5
|
+
> "close-but-wrong" embeddings.
|
|
6
|
+
> **Time:** ~10 minutes.
|
|
7
|
+
> **Prereqs:** runbook 08 (retrieval wired).
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
# agentforge.yaml
|
|
13
|
+
retrieval:
|
|
14
|
+
mode: hybrid # one of: vector | hybrid | bm25
|
|
15
|
+
embedder:
|
|
16
|
+
driver: openai
|
|
17
|
+
config: {model: text-embedding-3-small}
|
|
18
|
+
vector_store:
|
|
19
|
+
driver: postgres # native FTS via tsvector + ts_rank_cd
|
|
20
|
+
config:
|
|
21
|
+
dsn: $POSTGRES_DSN
|
|
22
|
+
table: docs
|
|
23
|
+
hybrid:
|
|
24
|
+
alpha: 0.6 # weight on semantic (0=BM25-only, 1=vector-only)
|
|
25
|
+
top_k: 8
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Step by step
|
|
29
|
+
|
|
30
|
+
1. **Pick a vector store with native hybrid.** Every shipped
|
|
31
|
+
driver supports `lexical_search` as of v0.2:
|
|
32
|
+
- `postgres` — Postgres `tsvector` + `ts_rank_cd`
|
|
33
|
+
- `sqlite` — SQLite FTS5 + `bm25`
|
|
34
|
+
- `neo4j` — Neo4j fulltext index + `score`
|
|
35
|
+
- `surrealdb` — SurrealDB `DEFINE ANALYZER` + `SEARCH ANALYZER ... BM25`
|
|
36
|
+
- `in_memory` — pure-Python BM25 (good for tests)
|
|
37
|
+
2. **Set `retrieval.mode: hybrid`.** The `Retriever` then runs
|
|
38
|
+
the vector pass and the lexical pass in parallel and fuses
|
|
39
|
+
the result lists via Reciprocal Rank Fusion (RRF).
|
|
40
|
+
3. **Tune `alpha`.** Higher `alpha` weights vector recall;
|
|
41
|
+
lower weights BM25. Start at 0.6 and bisect against your
|
|
42
|
+
golden-set evaluator.
|
|
43
|
+
4. **(Optional) Add a reranker** on top — see runbook 17.
|
|
44
|
+
Hybrid → rerank is the canonical RAG quality stack.
|
|
45
|
+
5. **Index your corpus.** Postgres / SQLite need a one-time
|
|
46
|
+
index creation (the v0.2 migration framework handles this
|
|
47
|
+
— see runbook 20).
|
|
48
|
+
|
|
49
|
+
## Variations
|
|
50
|
+
|
|
51
|
+
- **bm25 only** — for keyword-dominant corpora (logs, code).
|
|
52
|
+
`mode: bm25` skips the embedder entirely.
|
|
53
|
+
- **Tenant-scoped hybrid** — pass `tenant_id` through the
|
|
54
|
+
retriever; both passes filter on it before fusion.
|
|
55
|
+
- **Async indexing** — large corpora benefit from a background
|
|
56
|
+
job that bulk-inserts then refreshes the lexical index.
|
|
57
|
+
|
|
58
|
+
## Troubleshooting
|
|
59
|
+
|
|
60
|
+
| Symptom | Cause | Fix |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| Hybrid returns same results as vector | `alpha=1.0` or BM25 index missing | tune `alpha` and confirm the lexical migration applied |
|
|
63
|
+
| Latency 2x vector-only | sequential passes | upgrade `agentforge-memory-*` to v0.2 — the passes run in parallel |
|
|
64
|
+
| `lexical_search not supported` | older driver version | upgrade the driver package |
|
|
65
|
+
| Top BM25 hits dominate | low `alpha` + short queries | raise `alpha` toward 0.7 |
|
|
66
|
+
|
|
67
|
+
## Related
|
|
68
|
+
|
|
69
|
+
- Runbook 08 — Add memory + retrieval
|
|
70
|
+
- Runbook 17 — Add a reranker
|
|
71
|
+
- Runbook 20 — Apply schema migrations
|
|
72
|
+
- Feature spec: `docs/features/feat-022-hybrid-search.md`
|
|
73
|
+
- Feature spec: `docs/features/feat-025-neo4j-vector-store.md`
|
|
74
|
+
|
|
75
|
+
<!-- agentforge:end-managed -->
|
|
76
|
+
|
|
77
|
+
<!-- agentforge:custom -->
|
|
78
|
+
<!-- agentforge:end-custom -->
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# 19 — Add GraphRAG (graph-augmented retrieval)
|
|
2
|
+
|
|
3
|
+
> **Goal:** enrich top-k retrieval with N-hop neighbours from a
|
|
4
|
+
> graph store, so an answer about "Alice" also pulls in
|
|
5
|
+
> "Alice's manager" and "Alice's recent project".
|
|
6
|
+
> **Time:** ~15 minutes.
|
|
7
|
+
> **Prereqs:** runbooks 08 + 18. A populated `GraphStore`.
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
# agentforge.yaml
|
|
13
|
+
retrieval:
|
|
14
|
+
mode: hybrid
|
|
15
|
+
embedder:
|
|
16
|
+
driver: openai
|
|
17
|
+
config: {model: text-embedding-3-small}
|
|
18
|
+
vector_store:
|
|
19
|
+
driver: postgres
|
|
20
|
+
config: {dsn: $POSTGRES_DSN, table: docs}
|
|
21
|
+
graph_expansion: # the new bit
|
|
22
|
+
graph_store: people_graph # named graph store from modules.graph_stores
|
|
23
|
+
max_hops: 1
|
|
24
|
+
relations: [reports_to, works_on]
|
|
25
|
+
score_decay: 0.5 # neighbour score = parent_score * decay
|
|
26
|
+
top_k: 12
|
|
27
|
+
modules:
|
|
28
|
+
graph_stores:
|
|
29
|
+
people_graph:
|
|
30
|
+
driver: neo4j
|
|
31
|
+
config: {uri: $NEO4J_URI, user: neo4j, password: $NEO4J_PASSWORD}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Step by step
|
|
35
|
+
|
|
36
|
+
1. **Decide which entities you'll expand.** Pick a small set
|
|
37
|
+
of relation types (`reports_to`, `cited_by`, ...). Expanding
|
|
38
|
+
on everything destroys precision; one or two hops is enough.
|
|
39
|
+
2. **Populate the graph.** Either pre-build it (ETL job) or use
|
|
40
|
+
the agent itself: the `agentforge_core.contracts.graph`
|
|
41
|
+
surface has `add_triple` / `query_neighbors`.
|
|
42
|
+
3. **Add a `graph_expansion:` block** under `retrieval:`. The
|
|
43
|
+
`Retriever` runs vector / hybrid first, then expands each
|
|
44
|
+
top-k hit's entity via the named `GraphStore`, applies the
|
|
45
|
+
`score_decay` per hop, and re-ranks the merged list.
|
|
46
|
+
4. **Set `max_hops` conservatively.** Start with `1`; `2` is
|
|
47
|
+
for "explain why" queries where context chains matter.
|
|
48
|
+
5. **Tune `score_decay`.** `0.5` halves a neighbour's score
|
|
49
|
+
versus its parent at hop 1; lower decay = neighbours
|
|
50
|
+
compete; higher decay = neighbours augment without
|
|
51
|
+
overwhelming.
|
|
52
|
+
|
|
53
|
+
## Variations
|
|
54
|
+
|
|
55
|
+
- **Composes with anything else.** GraphRAG is post-retrieve,
|
|
56
|
+
so it works with `mode: vector` or `hybrid`, with or without
|
|
57
|
+
a reranker (rerank happens after expansion).
|
|
58
|
+
- **Sparse expansion** — only expand the top-1 hit
|
|
59
|
+
(`expand_top_n: 1`) when you want extra context only for the
|
|
60
|
+
best match.
|
|
61
|
+
- **Different graph per tenant** — name several graph stores
|
|
62
|
+
in `modules.graph_stores` and switch via dotted-path env
|
|
63
|
+
overrides (`AGENTFORGE_RETRIEVAL__GRAPH_EXPANSION__GRAPH_STORE=tenant_graph`).
|
|
64
|
+
|
|
65
|
+
## Troubleshooting
|
|
66
|
+
|
|
67
|
+
| Symptom | Cause | Fix |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| No neighbours returned | entity IDs in vector store don't match graph node IDs | confirm the `id` field matches across stores |
|
|
70
|
+
| Latency 3-4x | hop 2+ on a wide graph | drop `max_hops` to 1, narrow `relations` |
|
|
71
|
+
| Neighbour scores too high | `score_decay` ≥ 0.8 | lower toward 0.5 |
|
|
72
|
+
| Wrong neighbours | over-broad `relations` list | pin to the specific relations the query cares about |
|
|
73
|
+
|
|
74
|
+
## Related
|
|
75
|
+
|
|
76
|
+
- Runbook 08 — Add memory + retrieval
|
|
77
|
+
- Runbook 18 — Add hybrid search
|
|
78
|
+
- Feature spec: `docs/features/feat-023-graphrag-hybrid.md`
|
|
79
|
+
|
|
80
|
+
<!-- agentforge:end-managed -->
|
|
81
|
+
|
|
82
|
+
<!-- agentforge:custom -->
|
|
83
|
+
<!-- agentforge:end-custom -->
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# 20 — Apply schema migrations
|
|
2
|
+
|
|
3
|
+
> **Goal:** evolve a persistent store's schema (Postgres /
|
|
4
|
+
> SQLite / Neo4j / SurrealDB) without losing data or
|
|
5
|
+
> hand-running SQL on production.
|
|
6
|
+
> **Time:** ~10 minutes for the first migration.
|
|
7
|
+
> **Prereqs:** runbook 08 (a persistent store wired in).
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# 1. Generate a migration file from the template.
|
|
13
|
+
agentforge db migrate new "add_user_role_column"
|
|
14
|
+
|
|
15
|
+
# 2. Edit the generated migration:
|
|
16
|
+
# .agentforge/migrations/postgres/20260514T101200__add_user_role_column.sql
|
|
17
|
+
# Add your DDL/DML under -- up
|
|
18
|
+
|
|
19
|
+
# 3. Apply pending migrations.
|
|
20
|
+
agentforge db migrate up
|
|
21
|
+
|
|
22
|
+
# 4. Confirm.
|
|
23
|
+
agentforge db migrate status
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Step by step
|
|
27
|
+
|
|
28
|
+
1. **Pick the target store.** Migrations are per-driver. The
|
|
29
|
+
v0.2 framework ships:
|
|
30
|
+
- Postgres (`.agentforge/migrations/postgres/*.sql`)
|
|
31
|
+
- SQLite (`.agentforge/migrations/sqlite/*.sql`)
|
|
32
|
+
- Neo4j (`.agentforge/migrations/neo4j/*.cypher`)
|
|
33
|
+
- SurrealDB (`.agentforge/migrations/surrealdb/*.surql`)
|
|
34
|
+
2. **Generate a migration.** `agentforge db migrate new
|
|
35
|
+
"<description>"` writes a timestamped file under the
|
|
36
|
+
appropriate driver directory.
|
|
37
|
+
3. **Author the `-- up` block.** Use idempotent DDL where you
|
|
38
|
+
can (`CREATE INDEX IF NOT EXISTS ...`); the migrator tracks
|
|
39
|
+
applied state in a `__agentforge_migrations` table.
|
|
40
|
+
4. **Parameterise dimension-sensitive schemas.** Postgres
|
|
41
|
+
`vector(N)` and SurrealDB `HNSW DIMENSION N` should use
|
|
42
|
+
`${VECTOR_DIM}` so the same migration adapts to
|
|
43
|
+
3-small (1536) vs 3-large (3072) without forking:
|
|
44
|
+
|
|
45
|
+
```sql
|
|
46
|
+
-- up
|
|
47
|
+
CREATE TABLE docs (
|
|
48
|
+
id text primary key,
|
|
49
|
+
text text,
|
|
50
|
+
embedding vector(${VECTOR_DIM})
|
|
51
|
+
);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Set `${VECTOR_DIM}` via `agentforge db migrate up
|
|
55
|
+
--var VECTOR_DIM=1536` or in `agentforge.yaml`
|
|
56
|
+
under `db.migrations.variables`.
|
|
57
|
+
5. **Run `up`.** `agentforge db migrate up` applies every
|
|
58
|
+
pending migration in timestamp order, in a transaction
|
|
59
|
+
per file (where the driver supports it).
|
|
60
|
+
6. **Verify with `status`.** `agentforge db migrate status`
|
|
61
|
+
prints applied + pending counts.
|
|
62
|
+
|
|
63
|
+
## Variations
|
|
64
|
+
|
|
65
|
+
- **CI gate.** Add `agentforge db migrate status
|
|
66
|
+
--fail-on-pending` to your deploy pipeline to refuse a
|
|
67
|
+
rollout if migrations are unapplied.
|
|
68
|
+
- **Multiple stores.** Run `migrate up` per-store; the CLI
|
|
69
|
+
detects available drivers via your config.
|
|
70
|
+
- **`init_schema` shim** — pre-v0.2 agents that called
|
|
71
|
+
`MemoryStore.init_schema()` still work; you can opt-out by
|
|
72
|
+
setting `db.init_schema: false` and switching to migrations.
|
|
73
|
+
|
|
74
|
+
## Troubleshooting
|
|
75
|
+
|
|
76
|
+
| Symptom | Cause | Fix |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `MigrationConflict` | two devs added migrations with the same timestamp | regenerate one to bump the timestamp |
|
|
79
|
+
| Migration applied but data missing | non-idempotent up that crashed mid-flight | manually fix; record entry in `__agentforge_migrations` |
|
|
80
|
+
| `${VAR} not substituted` | variable not declared | pass `--var VAR=value` or add to `db.migrations.variables` |
|
|
81
|
+
| Dim mismatch on `vector(N)` | upgrading embedding model | new migration that recreates the column with the new dim |
|
|
82
|
+
|
|
83
|
+
## Related
|
|
84
|
+
|
|
85
|
+
- Runbook 08 — Add memory + retrieval
|
|
86
|
+
- Runbook 18 — Add hybrid search
|
|
87
|
+
- Feature spec: `docs/features/feat-024-schema-migrations.md`
|
|
88
|
+
|
|
89
|
+
<!-- agentforge:end-managed -->
|
|
90
|
+
|
|
91
|
+
<!-- agentforge:custom -->
|
|
92
|
+
<!-- agentforge:end-custom -->
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# 21 — Use streaming guardrails (sentence-window)
|
|
2
|
+
|
|
3
|
+
> **Goal:** stream tokens to your end-user while still running
|
|
4
|
+
> the output guardrails — so PII or unsafe content never
|
|
5
|
+
> reaches the client mid-stream.
|
|
6
|
+
> **Time:** ~5 minutes.
|
|
7
|
+
> **Prereqs:** runbooks 11 + 20 (chat sessions + guardrails).
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
# agentforge.yaml
|
|
13
|
+
modules:
|
|
14
|
+
chat:
|
|
15
|
+
session:
|
|
16
|
+
safety_mode: sentence-window # the new bit
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
async for chunk in session.stream(turn):
|
|
21
|
+
# `chunk.content` is sentence-sized — never per-token.
|
|
22
|
+
# Output validators have already vetted (and possibly
|
|
23
|
+
# redacted) the text before you see it.
|
|
24
|
+
await client.send(chunk.content)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Step by step
|
|
28
|
+
|
|
29
|
+
1. **Decide your latency / safety trade-off.** Three
|
|
30
|
+
`safety_mode` settings on `ChatSession`:
|
|
31
|
+
- `buffer-then-stream` (default) — agent finishes, output
|
|
32
|
+
validators run once on the full answer, the chat layer
|
|
33
|
+
segments the result for the wire. Safe, smooth — but the
|
|
34
|
+
user sees zero tokens until the run finishes.
|
|
35
|
+
- `sentence-window` — each token is buffered until a
|
|
36
|
+
sentence boundary; the completed sentence runs through
|
|
37
|
+
`OutputValidator.check_output`; the validated text emits
|
|
38
|
+
downstream. Mid-latency, safe.
|
|
39
|
+
- `stream-then-redact` — currently aliases sentence-window;
|
|
40
|
+
v0.3 may add inline-regex redaction without buffering.
|
|
41
|
+
2. **Set `modules.chat.session.safety_mode: sentence-window`**
|
|
42
|
+
in `agentforge.yaml`. `build_chat_session_from_config`
|
|
43
|
+
forwards the value into `ChatSession(safety_mode=...)`.
|
|
44
|
+
3. **Make sure your `OutputValidator`s are sentence-friendly.**
|
|
45
|
+
Validators like `pii_redact_basic` work per-sentence
|
|
46
|
+
naturally. Validators that need the full answer (e.g.,
|
|
47
|
+
policy-level "did the model commit to anything risky?")
|
|
48
|
+
should stay on `buffer-then-stream`.
|
|
49
|
+
4. **Test against a known leak.** Drive the agent with a
|
|
50
|
+
prompt that should produce a redacted string mid-output
|
|
51
|
+
(e.g., an API key); assert your sentinel never appears in
|
|
52
|
+
any streamed `ChatChunk`.
|
|
53
|
+
|
|
54
|
+
## Variations
|
|
55
|
+
|
|
56
|
+
- **Custom boundary regex.** Subclass `ChatSession` and
|
|
57
|
+
override `_make_window_buffer` to tweak the sentence
|
|
58
|
+
segmentation (English `.!?` default).
|
|
59
|
+
- **Stream-then-redact (v0.3).** When inline regex redaction
|
|
60
|
+
lands, `stream-then-redact` will skip buffering for
|
|
61
|
+
validators that don't need cross-token context.
|
|
62
|
+
- **Multi-language.** Today's segmentation is English-centric;
|
|
63
|
+
CJK / Arabic users should stay on `buffer-then-stream`
|
|
64
|
+
until v0.3 multi-language support ships.
|
|
65
|
+
|
|
66
|
+
## Troubleshooting
|
|
67
|
+
|
|
68
|
+
| Symptom | Cause | Fix |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| Stream looks delayed | sentence boundaries far apart in long unpunctuated text | the 200-char hard cap fires automatically; check the validator isn't slow |
|
|
71
|
+
| Redacted text appears partially | a validator that mutates token-by-token | switch the validator to a sentence-level check |
|
|
72
|
+
| `GuardrailViolation` stops the stream | violator surfaced from a per-sentence check | document the policy to the user; surface as a polite refusal chunk |
|
|
73
|
+
|
|
74
|
+
## Related
|
|
75
|
+
|
|
76
|
+
- Runbook 11 — Add safety guardrails
|
|
77
|
+
- Feature spec: `docs/features/feat-020-chat-agents.md`
|
|
78
|
+
|
|
79
|
+
<!-- agentforge:end-managed -->
|
|
80
|
+
|
|
81
|
+
<!-- agentforge:custom -->
|
|
82
|
+
<!-- agentforge:end-custom -->
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# {{ project_slug }} — runbook index
|
|
2
|
+
|
|
3
|
+
This directory carries the AgentForge runbooks for
|
|
4
|
+
**{{ project_slug }}**. Every runbook is task-oriented: one job,
|
|
5
|
+
one ~5-minute read, ready-to-copy code.
|
|
6
|
+
|
|
7
|
+
## Day-1 reading
|
|
8
|
+
|
|
9
|
+
| # | Title |
|
|
10
|
+
|---|---|
|
|
11
|
+
| 01 | [Set up a new agent](./01-set-up-new-agent.md) |
|
|
12
|
+
| 02 | [Add a tool](./02-add-a-tool.md) |
|
|
13
|
+
| 05 | [Write prompts](./05-write-prompts.md) |
|
|
14
|
+
| 06 | [Test your agent](./06-test-your-agent.md) |
|
|
15
|
+
|
|
16
|
+
## When you need it
|
|
17
|
+
|
|
18
|
+
| # | Title |
|
|
19
|
+
|---|---|
|
|
20
|
+
| 03 | [Add a pipeline task](./03-add-a-pipeline-task.md) |
|
|
21
|
+
| 04 | [Pick a reasoning strategy](./04-pick-reasoning-strategy.md) |
|
|
22
|
+
| 07 | [Debug a run](./07-debug-a-run.md) |
|
|
23
|
+
| 08 | [Add memory / persistence](./08-add-memory.md) |
|
|
24
|
+
| 09 | [Add MCP servers](./09-add-mcp.md) |
|
|
25
|
+
| 10 | [Add evaluators](./10-add-evaluators.md) |
|
|
26
|
+
| 11 | [Add safety guardrails](./11-add-safety-guardrails.md) |
|
|
27
|
+
| 12 | [Add observability](./12-add-observability.md) |
|
|
28
|
+
| 13 | [Configure multi-provider](./13-configure-multi-provider.md) |
|
|
29
|
+
|
|
30
|
+
## Retrieval & RAG
|
|
31
|
+
|
|
32
|
+
| # | Title |
|
|
33
|
+
|---|---|
|
|
34
|
+
| 17 | [Add a reranker](./17-add-reranker.md) |
|
|
35
|
+
| 18 | [Add hybrid search (BM25 + vector)](./18-add-hybrid-search.md) |
|
|
36
|
+
| 19 | [Add GraphRAG (graph-augmented retrieval)](./19-add-graphrag.md) |
|
|
37
|
+
|
|
38
|
+
## Streaming & schema
|
|
39
|
+
|
|
40
|
+
| # | Title |
|
|
41
|
+
|---|---|
|
|
42
|
+
| 20 | [Apply schema migrations](./20-apply-schema-migrations.md) |
|
|
43
|
+
| 21 | [Use streaming guardrails (sentence-window)](./21-use-streaming-guardrails.md) |
|
|
44
|
+
|
|
45
|
+
## Operations
|
|
46
|
+
|
|
47
|
+
| # | Title |
|
|
48
|
+
|---|---|
|
|
49
|
+
| 14 | [Deploy your agent](./14-deploy-your-agent.md) |
|
|
50
|
+
| 15 | [Upgrade your agent](./15-upgrade-your-agent.md) |
|
|
51
|
+
| 16 | [Configuration reference](./16-configuration-reference.md) |
|
|
52
|
+
|
|
53
|
+
## Conventions
|
|
54
|
+
|
|
55
|
+
- Every runbook is managed by the framework — opening it for
|
|
56
|
+
edit, you'll see a `<!-- agentforge:end-managed -->` marker.
|
|
57
|
+
Add project-specific notes below it; `agentforge upgrade`
|
|
58
|
+
preserves your section.
|
|
59
|
+
- AI assistants read `AGENTS.md` at the project root before
|
|
60
|
+
suggesting changes. The runbooks are the long-form companion
|
|
61
|
+
to that file.
|
|
62
|
+
- `agentforge docs` opens any runbook by topic (e.g.
|
|
63
|
+
`agentforge docs add-tool` jumps to runbook 02).
|
|
64
|
+
|
|
65
|
+
<!-- agentforge:end-managed -->
|
|
66
|
+
|
|
67
|
+
<!-- agentforge:custom -->
|
|
68
|
+
<!-- agentforge:end-custom -->
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# {{ project_name }}
|
|
2
|
+
|
|
3
|
+
{{ description }} Reviews a diff and emits structured `SimpleFinding`s.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
uv sync
|
|
7
|
+
cp .env.example .env
|
|
8
|
+
python -m {{ project_slug | replace('-', '_') }} "review the diff at /path/to.patch"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Drop in evaluator graders (faithfulness / correctness) once you
|
|
12
|
+
have rubric examples — see the commented stub in `agentforge.yaml`.
|
|
@@ -0,0 +1,23 @@
|
|
|
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 are a careful code reviewer. For each issue, emit a
|
|
6
|
+
SimpleFinding with severity, category, file, line, and a
|
|
7
|
+
concrete recommendation. Stay grounded in the diff — don't
|
|
8
|
+
invent issues that aren't present.
|
|
9
|
+
budget:
|
|
10
|
+
usd: 3.0
|
|
11
|
+
max_iterations: 30
|
|
12
|
+
|
|
13
|
+
modules:
|
|
14
|
+
evaluators:
|
|
15
|
+
# Wire the geval-based judges once you have rubric examples.
|
|
16
|
+
# - name: faithfulness
|
|
17
|
+
# config:
|
|
18
|
+
# sources_field: "retrieved_docs"
|
|
19
|
+
[]
|
|
20
|
+
|
|
21
|
+
logging:
|
|
22
|
+
level: "INFO"
|
|
23
|
+
format: "text"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Copier template: code-reviewer agent (feat-011).
|
|
2
|
+
#
|
|
3
|
+
# Reviews code diffs / PRs and emits SimpleFinding issues. Wires
|
|
4
|
+
# a file_read tool by default so the reviewer can pull source
|
|
5
|
+
# context. Add evaluator graders (faithfulness, correctness) as
|
|
6
|
+
# you build out the rubric.
|
|
7
|
+
|
|
8
|
+
_min_copier_version: "9.4.0"
|
|
9
|
+
_answers_file: ".agentforge-state/answers.yml"
|
|
10
|
+
_templates_suffix: ""
|
|
11
|
+
|
|
12
|
+
project_name:
|
|
13
|
+
type: str
|
|
14
|
+
help: "Human-readable project name."
|
|
15
|
+
default: "Code Reviewer"
|
|
16
|
+
|
|
17
|
+
project_slug:
|
|
18
|
+
type: str
|
|
19
|
+
help: "Project slug (kebab-case)."
|
|
20
|
+
default: "code-reviewer"
|
|
21
|
+
|
|
22
|
+
llm_provider:
|
|
23
|
+
type: str
|
|
24
|
+
choices:
|
|
25
|
+
Bedrock (AWS): bedrock
|
|
26
|
+
Anthropic (direct): anthropic
|
|
27
|
+
OpenAI: openai
|
|
28
|
+
default: bedrock
|
|
29
|
+
|
|
30
|
+
description:
|
|
31
|
+
type: str
|
|
32
|
+
default: "A code-reviewing AgentForge agent."
|
|
33
|
+
|
|
34
|
+
_template_name: "code-reviewer"
|
|
@@ -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,32 @@
|
|
|
1
|
+
"""Entry point for {{ project_name }}.
|
|
2
|
+
|
|
3
|
+
Reviews a diff. The agent's tools include `file_read` so it can
|
|
4
|
+
pull source context beyond what the diff shows. Outputs are
|
|
5
|
+
typically `SimpleFinding`s (feat-008) — see `agentforge.findings`.
|
|
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,14 @@
|
|
|
1
|
+
# {{ project_name }}
|
|
2
|
+
|
|
3
|
+
{{ description }} Answers questions from documentation / source.
|
|
4
|
+
Outputs `NarrativeFinding`s — markdown prose with citations.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
uv sync
|
|
8
|
+
cp .env.example .env
|
|
9
|
+
python -m {{ project_slug | replace('-', '_') }} "how does the auth flow work?"
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
For real RAG, install `agentforge-memory-sqlite` (or postgres),
|
|
13
|
+
wire `modules.memory.driver` and `modules.retriever` in
|
|
14
|
+
`agentforge.yaml`, and index your corpus.
|
|
@@ -0,0 +1,19 @@
|
|
|
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 answer questions using only the supplied documentation /
|
|
6
|
+
source. Emit a `NarrativeFinding` with the answer as `body`
|
|
7
|
+
(markdown ok), every citation in `references` (path:line or
|
|
8
|
+
URL), and `category="answer"`. Never speculate beyond the
|
|
9
|
+
sources.
|
|
10
|
+
budget:
|
|
11
|
+
usd: 2.0
|
|
12
|
+
max_iterations: 30
|
|
13
|
+
|
|
14
|
+
# When you have a corpus to index, wire memory.driver: sqlite (or
|
|
15
|
+
# postgres) and modules.retriever to enable RAG.
|
|
16
|
+
|
|
17
|
+
logging:
|
|
18
|
+
level: "INFO"
|
|
19
|
+
format: "text"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Copier template: docs-qa agent (feat-011).
|
|
2
|
+
#
|
|
3
|
+
# Answers questions from documentation / source. Emits
|
|
4
|
+
# NarrativeFinding (feat-008) — markdown prose with citations.
|
|
5
|
+
# Add a vector store + Retriever once you have a corpus to index.
|
|
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: "Docs QA"
|
|
14
|
+
|
|
15
|
+
project_slug:
|
|
16
|
+
type: str
|
|
17
|
+
default: "docs-qa"
|
|
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 docs / source Q&A AgentForge agent."
|
|
30
|
+
|
|
31
|
+
_template_name: "docs-qa"
|
|
@@ -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('-', '_') }}"]
|