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.
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 contract | In CrewAI |
|---|---|
job_title, mission | the agent's role and goal |
never, person_only | the agent's backstory |
| the complete contract, the required documents in full, task evidence | read again before every task and given to that task as its context (below) |
disallowed_tools | removed from the tools the task runs with (below) |
gates, never_checks | a task guardrail (below) |
| actions only a person takes | CrewAI'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"])))
- A passing output stays the task's output. A blocked output, or one that needs a person's decision, raises
OutputGateStoppedfromcrew.kickoff(). It is not sent back to the model to try again. - A claim that something ran, such as tests passing, needs evidence your host saw in this run, passed through the
evidencecallback. The model's own word never counts.
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:
- The role's block reaches the model in the task message, which is where CrewAI puts task context, not in the system message.
- CrewAI sends tool schemas in strict mode, so every argument is listed as required. Names, descriptions, defaults and bounds are the same as in the other adapters.
- Inside a crew, a tool error such as a wrong key or space reaches the model as CrewAI's tool error and the run goes on. With
tool_failure_policy="raise"on the agent, the crew stops instead.
Every helper
Import them from vinc.integrations.crewai. The functions whose names start with a take an AsyncVincClient and are awaited.
| Helper | Parameters | What it does |
|---|---|---|
create_agent | client, 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 arguments | A CrewAI agent bound to one role. Pass async_client as well for crew.akickoff(). |
create_vinc_tools | client, async_client, max_chars, cap | The read-only tools vinc_brief and vinc_search. |
create_output_guardrail | gate, evidence | A task guardrail. |
VincPersonOnlyInput | gate, role_id, run_id, decide, tasks, evidence | The human input step for person-only actions. result(task) returns what released the answer. |
prefetch, aprefetch | client, role_id, task, space, max_chars, pin | The role's required reading and task evidence. |
record_run, arecord_run | client, run_id, role, outcome, the other arguments of the client's record_run | One run as an episode. Needs a write key. |
record_episode, arecord_episode | client, title, summary, about, date, domain, tags, space, episode_id, create_only, operation_id | One episode. Needs a write key. |
write_package, awrite_package | client, question, conclusion, claims, produced_by, run_id, space, package_id, create_only, operation_id | One handoff package Record. Needs a write key; space is required. |
get_vinc_package_context, aget_vinc_package_context | client, package_id, space, max_chars | A handoff package, read as a block. |
Over MCP instead
To reach Vinc from CrewAI without this adapter, connect to the MCP server (Other frameworks).