Agentic loops
Portable engineering policies for coding agents — git, testing, logging, and language conventions written once and referenced everywhere
npx -y skills add andr-ca/agentharness --skill agentic-loopsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 25 days oldThe repository was created 25 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when building multi-turn agents, tool-calling systems, agent orchestration, or autonomous workflows — covers loops, tool calling, branching, reflection patterns.
SKILL.md
8.3 KB, as published. Nobody here has run it
Agentic Loops: Agents, Tools, Workflows
Structured patterns for building multi-turn agents that reason, act, and observe.
An agentic loop is:
- Think: Agent reasons about task → decides action
- Act: Call tools / take action
- Observe: Get result, update state
- Repeat: Loop until task complete
Minimal Loop — use the tested implementation, don't hand-roll this
Don't write a bespoke think/act/observe loop from scratch — it's easy to
get the tool-result protocol wrong (feeding a tool's result back as a
plain "user" message loses the call binding and looks like human input
to the model, instead of {"role": "tool", "tool_call_id": ..., ...}),
easy to leave out a budget (infinite loop if the model never stops
calling tools), and easy to skip argument validation (a malformed tool
call reaches your tool function instead of being rejected).
agent_loop.py, bundled alongside this file (a symlink back to
patterns/agentic-loops/agent_loop.py, so it resolves whether you
installed the whole harness or only this one skill), is a minimal, tested
(100% coverage), provider-neutral implementation that gets these right:
JSON-Schema-validated arguments, provider-correct tool-result messages, an
iteration + wall-clock budget, an optional approval hook, and an auditable
trace that never logs raw tool output. See
patterns/agentic-loops/README.md in the full harness checkout for the
complete usage example and what it does not cover (sandboxing,
prompt-injection handling, real cost accounting, cancellation,
retries/idempotency, persistence, evals) — that guide isn't bundled with
this skill since it's documentation, not something the skill needs to
function.
# Run from this skill's own directory, or add it to sys.path — see
# test_agent_loop.py (also bundled here) for a runnable example.
from agent_loop import Budget, ToolSpec, run_agent_loop
tool = ToolSpec(name="add", fn=add, parameters_schema={...}) # JSON Schema
result = run_agent_loop(
model_fn=my_provider_adapter, # translates to/from your provider's native shape
tools={"add": tool},
messages=[{"role": "user", "content": "What is 2 + 3?"}],
budget=Budget(max_iterations=5, max_seconds=30),
)
Tool Definition
class Tool:
def __init__(self, name: str, fn, description: str):
self.name = name
self.fn = fn
self.description = description
def call(self, **kwargs):
"""Call tool and return result as JSON string."""
try:
result = self.fn(**kwargs)
return json.dumps(result) if not isinstance(result, str) else result
except TypeError as e:
return json.dumps({"error": f"Invalid arguments: {e}"})
# Define tools
def search_web(query: str) -> dict:
"""Search the web for information."""
# Implementation
return {"results": [...]}
def read_file(path: str) -> str:
"""Read a file's contents."""
with open(path) as f:
return f.read()
# Registry
tools = {
"search_web": Tool("search_web", search_web, "Search the web"),
"read_file": Tool("read_file", read_file, "Read file contents"),
}
Patterns
Pattern 1: Reflection
Agent observes its own results and corrects course.
def run_with_reflection(agent, task):
"""Agent reflects on each step."""
state = {"task": task, "iteration": 0}
for i in range(5):
# Execute action
action = agent.decide(state)
result = execute(action)
# Reflect on result
reflection = agent.reflect(state, action, result)
if reflection["progress"]:
state["iteration"] += 1
else:
# Adjust strategy based on reflection
state["strategy"] = reflection["new_strategy"]
return state
Pattern 2: Tool Chaining
One tool's output → next tool's input.
def chain_tools(tool_sequence: list, initial_input):
"""Execute tools in sequence."""
result = initial_input
for tool_name in tool_sequence:
tool = tools[tool_name]
result = tool(result) # Output of one → input of next
return result
# Usage
chain_tools(
["fetch_data", "transform_data", "save_data"],
initial_input="/input"
)
Pattern 3: Branching
Agent branches logic based on intermediate results.
def run_with_branching(agent, task):
"""Agent chooses execution path."""
# Classify task
classification = agent.classify(task)
if classification == "simple":
return agent.solve_direct(task)
elif classification == "complex":
# Multi-step reasoning
return agent.solve_step_by_step(task)
elif classification == "data_heavy":
# Fetch data first
data = agent.gather_data(task)
return agent.solve_with_data(task, data)
Pattern 4: Multi-Agent Consensus
Multiple agents vote on best action.
def consensus_decision(agents: list, task: str):
"""Multiple agents propose; choose by vote."""
proposals = []
for agent in agents:
proposal = agent.propose(task)
proposals.append(proposal)
# Vote: most common proposal wins
votes = {}
for prop in proposals:
key = prop["action"]
votes[key] = votes.get(key, 0) + 1
best = max(votes, key=votes.get)
logger.info(f"Consensus: {best} (votes: {votes})")
return execute(best)
Caution: this is not independent validation. Agents sharing a model, prompt, or training data have correlated errors — they can confidently agree on the same wrong answer. Use it to reduce variance on tasks with genuinely diverse proposers, not as a correctness guarantee.
Common Pitfalls
| Pitfall | Cause | Fix |
|---|---|---|
| Infinite loops | Agent repeats same action | Add iteration limit |
| Token explosion | Long message history | Summarize old messages |
| Tool errors ignored | No error handling | Catch exceptions, feed back to agent |
| No observability | Can't debug | Log every action, decision, tool call |
| Silent failures | Errors don't propagate | Always return results to agent |
Observability
def run_with_logging(agent, task, logger):
"""Run agent with comprehensive logging."""
trace_id = uuid.uuid4()
logger.info("Agent start", extra={"trace_id": trace_id, "task": task})
state = {"messages": []}
for iteration in range(10):
action = agent.decide(state)
logger.info("Action", extra={
"trace_id": trace_id,
"iteration": iteration,
"action": action["name"],
})
result = call_tool(action)
logger.info("Result", extra={
"trace_id": trace_id,
"tool": action["name"],
"success": result["status"] == "ok",
})
state["messages"].append({"role": "user", "content": json.dumps(result)})
logger.info("Agent done", extra={"trace_id": trace_id, "iterations": len(state["messages"])})
return state
Checklist
- Define tools clearly (name, description, parameters)
- Add iteration limit (prevent infinite loops)
- Log every action, tool call, result
- Handle tool errors explicitly
- Feed errors back to agent (don't hide)
- Test with small iteration limits first
- Trace IDs for debugging
- Monitor token usage (watch for message explosion)
References
- Tested implementation:
agent_loop.py+test_agent_loop.py, bundled in this skill's own directory (works whether you installed the whole harness or only this skill). - Full guide (usage example, what's not covered, pseudocode patterns) —
needs the full harness checkout, not bundled here since it's
documentation rather than something this skill runs:
patterns/agentic-loops/README.md - Error handling:
.claude/skills/error-handling/SKILL.md - OpenAI Responses API — current tool-use API; the Assistants API is deprecated (sunset 2026-08-26)