# How to Feed Results Back into a Needle Agent Using `complete()`

> Learn how to feed results back into your Needle agent using the complete() function. Seamlessly integrate tool execution outputs for continuous generation.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: how-to-guide
- Published: 2026-09-05

---

**Call `agent.complete()` with a JSON payload containing a `"results"` key holding your tool execution outputs, and the model will continue generating as if it received those results natively.**

The Needle SDK from cactus-compute/needle provides both high-level automation and low-level control for tool-calling agents. While `agent.run()` handles the entire execute-and-feed-back cycle automatically, understanding how to manually feed results back into `complete()` gives you precise control over multi-turn tool interactions, custom execution logic, and external integrations.

---

## The Two Entry Points: `complete()` vs `run()`

Needle exposes two primary methods for model interaction:

| Method | Purpose |
|--------|---------|
| `agent.complete(prompt)` | Sends a raw text prompt or JSON payload and receives a response that may contain `function_calls` |
| `agent.run(prompt)` | **Automates** the full cycle: calls `complete()`, executes tools, feeds results back, and repeats until done |

According to the Needle source code in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), the `run()` method (around line 202) constructs the feedback loop that gathers tool results, serializes them to JSON, and passes them to `_complete()` for the next generation round. For custom workflows, you replicate this pattern manually.

---

## The Manual Feedback Pattern

To feed results back using `complete()`, follow this sequence:

1. **Initial call** — Send your prompt with `agent.complete(prompt)`
2. **Execute tools** — Run each function in the returned `function_calls` array
3. **Build payload** — Create `{"results": [...]}` with your execution outputs
4. **Feed back** — Call `agent.complete(json.dumps(payload))` to continue the conversation

The engine recognizes the `"results"` key and treats the input as tool output rather than a new user prompt.

---

## Code Example: Manual Tool Loop with `complete()`

This example shows the complete manual flow without using `run()`:

```python
import json
import needle

# 1️⃣  Define a tool using the decorator

@needle.tool
def add(a: int, b: int):
    """Return the sum of two integers."""
    return {"sum": a + b}

# 2️⃣  Initialize the agent with available tools

agent = needle.Needle(tools=[add])

# 3️⃣  First completion — model may request tool execution

first_response = agent.complete("What is 7 + 5?")
print(first_response)

# Contains: {"function_calls": [{"name": "add", "arguments": {"a": 7, "b": 5}}]}

# 4️⃣  Execute the requested function calls yourself

calls = first_response.get("function_calls", [])
results = [add(**call["arguments"]) for call in calls]

# results = [{"sum": 12}]

# 5️⃣  Feed results back to continue the conversation

payload = {"results": results}
final_response = agent.complete(json.dumps(payload))
print(final_response)

# Model now provides: "7 + 5 = 12"

```

In [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py), the `complete()` method (lines 57‑59) forwards to `_complete()`, which handles the actual RPC to the native engine. The response parsing occurs at lines 74‑78, where the JSON envelope—including any `function_calls`—is extracted.

---

## When to Use `run()` Instead

For standard tool-casting without custom logic, `run()` eliminates boilerplate:

```python
import needle

@needle.tool
def get_weather(city: str):
    return {"city": city, "temp_c": 22, "sky": "sunny"}

agent = needle.Needle(tools=[get_weather])

# Fully automated loop

result = agent.run("What's the weather in Paris?")
print(result["results"])

# [{'city': 'Paris', 'temp_c': 22, 'sky': 'sunny'}]

```

The `run()` implementation in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) (around line 202) performs the same JSON serialization and `_complete()` call you would write manually, but wrapped in a robust retry and continuation loop.

---

## Feeding Custom or External Results

The manual approach shines when integrating external APIs or post-processing tool outputs:

```python
import json
import needle

def call_third_party_api(query: str):
    # Simulate external service

    return {"recommendation": "Use Needle for local LLM tool use"}

agent = needle.Needle()  # No tools registered — we're providing results externally

# Model asks for information we source elsewhere

initial = agent.complete("What tool should I use for local LLM agents?")

# Inject external knowledge directly

external_result = call_third_party_api("llm tool framework")
continuation = agent.complete(json.dumps({"results": [external_result]}))

print(continuation)

```

Since `complete()` accepts any properly formatted JSON string containing a `"results"` array, you're not limited to tools registered via `@needle.tool`.

---

## JSON Contract and Internal Flow

The low-level RPC to the native inference engine resides in [`needle/_worker.py`](https://github.com/cactus-compute/needle/blob/main/needle/_worker.py) (lines 8‑13), where the `complete` method serializes requests. The response format expected by `_complete()` includes:

- **`function_calls`** — array of requested tool invocations
- **`results`** — when sent in a request, triggers continuation mode

As documented in [`doc/apis.md`](https://github.com/cactus-compute/needle/blob/main/doc/apis.md), maintaining this contract ensures the engine correctly interprets whether you're initiating a new turn or continuing from tool output.

---

## Summary

- **Manual control**: Use `agent.complete()` with `{"results": [...]}` to feed tool outputs back and continue generation
- **Automation**: Use `agent.run()` when you want Needle to handle the entire execute-and-feed cycle
- **Implementation**: The feedback mechanism in [`needle/__init__.py`](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py) serializes results and passes them to `_complete()` (line 202) for native engine processing
- **Flexibility**: The `"results"` payload format supports custom data from any source, not just registered tools

---

## Frequently Asked Questions

### What format must the results payload use?

The payload must be a JSON object with a single `"results"` key containing an array of your tool return values. Example: `{"results": [{"sum": 12}]}`. The Needle engine recognizes this structure as tool output rather than a new user prompt.

### Can I mix manual `complete()` calls with `run()` in the same session?

Yes, though carefully. `run()` maintains internal state for its loop. If you interleave manual `complete()` calls, you may desynchronize the conversation history. For hybrid approaches, stick to manual `complete()` throughout or use `run()` exclusively.

### Why would I prefer `complete()` over the simpler `run()`?

Use `complete()` when you need: custom tool execution logic (async, remote APIs), result transformation before feedback, selective tool calling, or visibility into each generation step. The tradeoff is explicit state management versus `run()`'s convenience.

### Does `_complete()` differ from `complete()`?

`complete()` (public, lines 57‑59) adds bookkeeping before calling `_complete()` (internal), which performs the actual RPC. For most cases, use `complete()`. Access `_complete()` only if you're replicating `run()`'s loop behavior and need to bypass overhead.