Agent frameworks

CrewAI

vincs[crewai] builds a CrewAI agent from one Vinc role. Before every task the agent reads the role's contract and required documents in full, and its answer is checked against the role. It writes to Vinc only when your code asks.

Run the agent on a Task in a Crew. That is where it reads its role. Agent.kickoff outside a crew is refused, because it would run without the role.

Install

pip install "vincs[crewai]"

Requires Python 3.11 to 3.13, because CrewAI does not support 3.14. This extra installs CrewAI 1.15.3 or later. A read-only key (vinc_ro_) is enough to build and run the agent; only record_run, record_episode and write_package need a write key.

The CrewAI adapter has no separate package: import it from vinc.integrations.crewai.

Build an agent from a role

create_agent reads the role once and fills the agent's role, goal and backstory from it.

from crewai import Crew, Task
from vinc import Vinc
from vinc.integrations.crewai import create_agent

reader = Vinc()                                        # reads VINC_API_KEY; a vinc_ro_ key
role = reader.role("concept:agent-reviewer-judgement")
reviewer = create_agent(reader, role["id"], llm="gpt-4o", prefetch_pin=role["hash"])
review = Task(description="Review the diff in PR 12.", expected_output="Findings with their sources",
              agent=reviewer)
result = Crew(agents=[reviewer], tasks=[review]).kickoff()
Role contractIn CrewAI
job_title, missionthe agent's role and goal
never, person_onlythe agent's backstory
the complete contract, the required documents in full, task evidenceread again before every task and given to that task as its context (below)
disallowed_toolsremoved from the tools the task runs with (below)
gates, never_checksa task guardrail (below)
actions only a person takesCrewAI's human input step (below)

What every task reads

Every task the agent executes is one run. Before the model is called the run reads the role contract, every required document in full with its verified hash, and a brief and a search on the task: 1 call for the role, 2 per required document and 2 for the task. Nothing is cached between runs, and any failed read stops the task before the model is called.

The block is never cut. The contract and documents share a prefetch_max_chars budget of 24,000 characters; over it the task raises prefetch_budget_exceeded. Give a role with many documents a larger budget, such as prefetch_max_chars=120000.

With prefetch_pin set to a hash from role(), the read in create_agent and every task's read are held to that contract: a role that changed raises VincRoleChanged and the model is not called. The role, goal and backstory come from the read in create_agent; build a new agent to take a newer contract.

When CrewAI calls the agent again after an error inside a task, that retry belongs to the same run. It reads nothing again and keeps the same tools and call counts.

Tools the role does not allow

Tools named exactly as in the contract's disallowed_tools are removed from the tools the task runs with, so the model does not see them and CrewAI cannot run them. A model that calls one anyway gets CrewAI's own Tool not found. Entries that match none of the agent's tools give one warning per run.

To apply the role's capability policy, pass capability_map, which pairs tool names with capability ids. A tool whose capability the role denies is removed the same way. A tool whose capability needs approval stays visible, but a call to it gets a sentence asking for a person instead of running. budget.tool_calls caps all tool calls of a run.

Let the model look things up

create_agent(..., expose_tools=True) adds the read-only tools vinc_brief and vinc_search to every run, with 4 calls between them; the fifth gets a sentence saying the cap is reached. CrewAI runs the tool calls of one model turn at the same time, so when one turn goes over, which of its calls gets that sentence is not fixed; how many do is. To give only the tools to any CrewAI agent, use create_vinc_tools(client).

Check the answer against the role

create_output_guardrail turns the role's output gate into a task guardrail. It checks the task's output outside the model, against the role's gates, never_checks and person_only.

from vinc import OutputGate
from vinc.integrations.crewai import create_output_guardrail

review = Task(description="Review the diff in PR 12.", expected_output="Findings with their sources",
              agent=reviewer, guardrail=create_output_guardrail(OutputGate(role["props"])))

Ask a person before a person-only step

VincPersonOnlyInput answers CrewAI's human input step for the tasks you bind. Give each task human_input=True and the role output schema, and run the crew inside installed().

from vinc import ApprovalDecision, RoleOutput
from vinc.integrations.crewai import VincApprovalRejected, VincPersonOnlyInput

release = Task(description="Prepare the merge of PR 12.", expected_output="The review",
               agent=reviewer, human_input=True, output_pydantic=RoleOutput)

def decide(request):                                   # your own channel to a person
    return ApprovalDecision(request_id=request.request_id, approved=ask_a_person(request))

step = VincPersonOnlyInput(OutputGate(role["props"]), role_id=role["id"], run_id="release-1",
                           decide=decide, tasks=[release])
with step.installed():
    Crew(agents=[reviewer], tasks=[release]).kickoff()

A passing answer is released without asking anyone. An answer that needs a person calls decide once. Approval releases that answer unchanged and does not run or permit any tool; rejection raises VincApprovalRejected, and a blocked answer raises OutputGateStopped. None of these calls the model again. Tasks you do not bind keep CrewAI's own feedback prompt.

Hand off to another agent

The first agent writes one package Record with write_package. Build the next agent with package_id and package_space, and every task it runs reads the package again with its own key. Deleted or unreadable evidence is marked as not citable.

Record when the run ends

Nothing is written unless your code calls a write helper. When the crew run ends, record it with a write key. Keep the raw trace as a file and pass its trace_ref. A retry of the same run_id merges into the same episode.

import os
from vinc import Vinc, trace_ref
from vinc.integrations.crewai import record_run

writer = Vinc(api_key=os.environ["VINC_WRITE_KEY"])   # vinc_sk_
record_run(writer, "release-1", role=role["id"], outcome="success", host="crewai",
           steps=len(result.tasks_output), tokens_in=result.token_usage.prompt_tokens,
           tokens_out=result.token_usage.completion_tokens, trace=trace_ref("release-1.jsonl"))

record_episode and write_package take the same parameters as in the other adapters, including create_only and operation_id (Retry the record step).

Where CrewAI differs

In CrewAI these differ from the other two adapters:

Every helper

Import them from vinc.integrations.crewai. The functions whose names start with a take an AsyncVincClient and are awaited.

HelperParametersWhat it does
create_agentclient, role_id, llm, tools, async_client, prefetch_max_chars, prefetch_pin, expose_tools, tool_calls_per_run, max_chars, package_id, package_space, package_max_chars, capability_map, other Agent argumentsA CrewAI agent bound to one role. Pass async_client as well for crew.akickoff().
create_vinc_toolsclient, async_client, max_chars, capThe read-only tools vinc_brief and vinc_search.
create_output_guardrailgate, evidenceA task guardrail.
VincPersonOnlyInputgate, role_id, run_id, decide, tasks, evidenceThe human input step for person-only actions. result(task) returns what released the answer.
prefetch, aprefetchclient, role_id, task, space, max_chars, pinThe role's required reading and task evidence.
record_run, arecord_runclient, run_id, role, outcome, the other arguments of the client's record_runOne run as an episode. Needs a write key.
record_episode, arecord_episodeclient, title, summary, about, date, domain, tags, space, episode_id, create_only, operation_idOne episode. Needs a write key.
write_package, awrite_packageclient, question, conclusion, claims, produced_by, run_id, space, package_id, create_only, operation_idOne handoff package Record. Needs a write key; space is required.
get_vinc_package_context, aget_vinc_package_contextclient, package_id, space, max_charsA handoff package, read as a block.

Over MCP instead

To reach Vinc from CrewAI without this adapter, connect to the MCP server (Other frameworks).