brief-nest-38726
03/02/2026, 2:19 PMtall-airline-16756
03/02/2026, 2:19 PMtall-airline-16756
03/02/2026, 2:21 PMlangchain.agents.create_agent.
Quick acknowledgement: langchain.agents.create_agent is not supported by the LiveKit LangChain integration. LiveKit’s langchain plugin expects a compiled LangGraph (StateGraph -> Pregel) wrapped by LLMAdapter, so the recommended approach is to build a LangGraph workflow and use that as the LLM inside an AgentSession.
If that works for you, here’s a minimal Python example showing how to wire a compiled LangGraph into LiveKit using the livekit-plugins-langchain pattern.
1) Install the plugin
uv add "livekit-agents[langchain]~=1.4"
2) Minimal example (Python)
from langgraph.graph import StateGraph
from livekit.agents import AgentSession, Agent
from livekit.plugins import langchain
# 1) Define a simple LangGraph workflow and compile it
def create_workflow():
# Build a small StateGraph with nodes/edges according to your logic
graph = StateGraph()
# ... define nodes and transitions here ...
# return the compiled Pregel/graph expected by LLMAdapter
return graph.compile()
# 2) Create an AgentSession using the LLMAdapter with the compiled graph
session = AgentSession(
llm=langchain.LLMAdapter(
graph=create_workflow()
),
# add optional media components as needed:
# stt=..., tts=..., vad=...
)
# 3) Create an Agent that uses this session and attach to a LiveKit room
agent = Agent(session=session)
# 4) Start/attach the agent to a user session or room per your app flow
# (room connection / AgentSession lifecycle is handled by your app)
Notes and important points
• LLMAdapter expects a compiled LangGraph (Pregel) object — not a zero-shot or react agent produced by langchain.agents.create_agent.
• The LangGraph approach provides explicit state and multi-step workflows, which match LiveKit AgentSession’s stateful voice/agent model.
• This integration is available in Python.
If you were specifically looking to use langchain.agents.create_agent, tell me more about the agent style you need (zero-shot, tool usage, react-style, tools you want to plug in). I can:
• Suggest how to re-express that logic as a LangGraph workflow, or
• Show patterns for wrapping tool calls inside LangGraph nodes so you get similar behavior to a LangChain agent.
Which direction would you like: convert a create_agent workflow into LangGraph, or a deeper example of a LangGraph workflow integrated with LiveKit (including STT/TT S placeholders)?
Sources: LangChain integration guide | LiveKit Documentationbrief-nest-38726
03/02/2026, 2:22 PMtall-airline-16756
03/02/2026, 2:22 PMlangchain.agents.create_agent together with LiveKit. I’ll be direct: LiveKit’s LangChain plugin does not directly support agents created with langchain.agents.create_agent. However, you can get the same ReAct-style behavior by modeling the thinking/action loop inside a LangGraph workflow and using LiveKit’s LLMAdapter to run that workflow inside an AgentSession.
What that looks like and your options
• ReAct agents (zero-shot/react-style agents created with create_agent) are not supported directly by the LiveKit langchain plugin.
• You can reproduce ReAct behavior by creating LangGraph nodes that:
◦ Inspect the chat/context (the “reasoning” step).
◦ Call external tools (HTTP APIs, search, databases) as action nodes.
◦ Return observations back into the graph and continue reasoning.
• Wrap the compiled LangGraph (Pregel) with langchain.LLMAdapter and pass it to AgentSession. That gives you a stateful agent inside LiveKit that can:
◦ Use STT/TT S/VAD components for voice I/O,
◦ Maintain multi-step state,
◦ Call your tools as nodes in the workflow.
If you want, I can provide a concrete example. To tailor it, please tell me:
• Do you already have specific tools you call (e.g., a search API, a calendar API, or custom functions)? List them and how you call them.
• Do you want the reasoning and tool-call loop to run fully inside the graph, or do you prefer the graph to delegate tool calls to external services your app hosts?
• Which language/runtime are you using (you mentioned Python earlier — is that correct)?
If you want a quick starting example, I can: 1) Show a small LangGraph that implements a ReAct-like loop with one example tool node (HTTP search). 2) Show how to compile it and wire it to LLMAdapter and AgentSession. 3) Show how to map LiveKit chat messages into the graph’s inputs.
Which would be most helpful for you?brief-nest-38726
03/02/2026, 2:23 PMtall-airline-16756
03/02/2026, 2:23 PMLLMAdapter inside an AgentSession. That gives you the same observable ReAct behavior (thought → action → observation → thought) while remaining compatible with LiveKit’s stateful agent model.
Why that matters
• LiveKit’s plugin expects a compiled LangGraph (Pregel) object wrapped by LLMAdapter. langchain.agents.create_agent objects are not directly supported.
• Rebuilding ReAct inside LangGraph preserves the core pattern:
◦ A reasoning node produces the next “thought” or plan.
◦ An action node invokes a tool (HTTP API, search, calendar, DB).
◦ An observation node receives the tool response and feeds it back to reasoning.
◦ The loop repeats until a completion node produces the final response.
• Running this as a workflow gives you extra benefits: persistent state across turns, explicit control of retries/timeouts, and easier mapping to STT/TTS and VAD in LiveKit.
If you’d like, I can provide a concrete Python example that includes: 1) A minimal LangGraph that implements a ReAct-style loop with one example tool (e.g., HTTP search). 2) How to compile the graph and wrap it with langchain.LLMAdapter. 3) How to wire that into AgentSession (with placeholders for STT/TT S/VAD). 4) Example node designs for reasoning, action, and observation.
Which would you prefer?
• A short, minimal example to get started, or
• A more complete sample including tool call patterns, error handling, and LiveKit message mapping?
Also tell me if you already have a specific tool (search API, calendar, database) you want included in the example.brief-nest-38726
03/02/2026, 2:25 PMtall-airline-16756
03/02/2026, 2:27 PMLLMAdapter, and puts it inside an AgentSession. This demonstrates how to get the same behavior as a ReAct agent created with langchain.agents.create_agent, but in a form the LiveKit langchain plugin supports.
What this example includes
• A small tool: an HTTP “search” function (placeholder).
• LangGraph nodes: Reason (generate plan), Action (call tool), Observe (capture result), Decide (stop or continue).
• Compile the graph and wrap with langchain.LLMAdapter.
• Create an AgentSession and an Agent to run in LiveKit (STT/TTS placeholders included).
Prerequisites
• Python runtime
• Installed plugin: uv add "livekit-agents[langchain]~=1.4"
• LangGraph & LiveKit plugin libs available in your environment
Minimal example (Python)
# NOTE: this is a minimal illustrative example. Replace placeholders (LLM, STT, TTS, VAD)
# with your actual implementations and credentials.
from langgraph.graph import StateGraph, Node, compile_graph # hypothetical API names
from livekit.agents import AgentSession, Agent
from livekit.plugins import langchain as lk_langchain
# 1) Example tool: simple HTTP search (synchronous for example)
def http_search_tool(query: str) -> str:
# Replace with your real HTTP call (requests, httpx) and parsing
# Here we just return a stubbed response
return f"search results for: {query}"
# 2) Build a LangGraph with ReAct-style loop
def build_react_graph():
graph = StateGraph(name="react_workflow")
# Reason node: given conversation/history, produce `thought` and `action_instruction`
def reason_fn(inputs):
# inputs might include 'user_input' and 'history'
user_input = inputs.get("user_input", "")
# Produce an action instruction if query-like, otherwise final answer
# In real use, this should call LLM to produce structured output (thought + action)
# Here we use a simple heuristic for illustration
if "search:" in user_input:
# instruct action and pass the query
return {"thought": "I should search the web", "action_instruction": user_input.replace("search:", "").strip(), "done": False}
else:
return {"thought": "I can answer directly", "final_answer": f"Direct reply to: {user_input}", "done": True}
reason_node = Node("reason", func=reason_fn, outputs=["thought", "action_instruction", "final_answer", "done"])
# Action node: call the tool given action_instruction, produce `observation`
def action_fn(inputs):
action_instruction = inputs.get("action_instruction", "")
if not action_instruction:
return {"observation": None}
result = http_search_tool(action_instruction)
return {"observation": result}
action_node = Node("action", func=action_fn, inputs=["action_instruction"], outputs=["observation"])
# Observe/Decide node: incorporate observation and decide next step
def decide_fn(inputs):
done = inputs.get("done", False)
observation = inputs.get("observation", "")
if done:
return {"final_output": inputs.get("final_answer")}
# For demo: use observation to produce a final answer
final = f"Based on observation: {observation}"
return {"final_output": final}
decide_node = Node("decide", func=decide_fn, inputs=["done", "final_answer", "observation"], outputs=["final_output"])
# Wiring: reason -> action (if not done) -> decide -> maybe loop (simple single loop here)
graph.add_node(reason_node)
graph.add_node(action_node)
graph.add_node(decide_node)
# Connect nodes: reason -> action_instruction -> action
graph.connect("reason", "action", output_key="action_instruction", input_key="action_instruction")
# reason outputs done/final_answer into decide
graph.connect("reason", "decide", output_key="done", input_key="done")
graph.connect("reason", "decide", output_key="final_answer", input_key="final_answer")
# action observation -> decide
graph.connect("action", "decide", output_key="observation", input_key="observation")
# In a real looping workflow, you'd loop back to reason with observation if not done.
# For this minimal example we execute a single reason->action->decide cycle.
compiled = graph.compile() # compile to Pregel/compiled graph expected by LLMAdapter
return compiled
# 3) Wrap compiled LangGraph with LLMAdapter
def create_agent_session():
compiled_graph = build_react_graph()
# LLMAdapter wraps the compiled graph so LiveKit can execute it as the "LLM" provider.
llm_adapter = lk_langchain.LLMAdapter(graph=compiled_graph)
# Placeholder STT/TTS/VAD: replace with real implementations
stt = None # e.g., inference.STT(...)
tts = None # e.g., inference.TTS(...)
vad = None # e.g., silero.VAD.load()
session = AgentSession(
llm=llm_adapter,
stt=stt,
tts=tts,
vad=vad,
)
agent = Agent(session=session)
return agent
# 4) Example usage: feed user input into the workflow and run
if __name__ == "__main__":
agent = create_agent_session()
# How your app feeds input depends on your LiveKit integration.
# For a simple test, simulate invoking the compiled workflow directly:
compiled = build_react_graph()
# Most runtimes expose a run/execute method on the compiled graph:
result = compiled.run(inputs={"user_input": "search: latest tech news"})
print("Final result:", result.get("final_output"))
Key implementation notes and how this maps to ReAct
• Reason node = the LLM step that decides what to do next (thought + action instruction).
◦ In production, this node should call an LLM and return structured fields (thought, action to call, done flag, etc.).
• Action node = tool invocation (HTTP search, DB lookup, calendar).
◦ Implement tool calls in action nodes; return observations back to the graph.
• Observe/Decide node = merge observations and either finish or loop.
◦ For a full ReAct loop, feed the observation back into the Reason node and iterate until done == True.
• LLMAdapter expects a compiled LangGraph (Pregel) object — you cannot pass a langchain.agents.create_agent object directly.
• Running inside LiveKit:
◦ Use AgentSession to attach STT/TTS and VAD so the workflow can be driven by voice and respond with speech.
◦ Map LiveKit chat/message events to the workflow inputs (e.g., user_input) and stream results back to the room.
Next steps I can help with
• Turn the minimal example into a full looping ReAct graph (looping Reason after Observe) that runs until a termination condition.
• Show a concrete example of invoking an external HTTP tool using httpx and retry/error handling in an Action node.
• Show how to wire LiveKit event handlers to feed room messages and audio into the workflow and emit transcript/tts back into the room.
Which follow-up would you like: implement the full loop with iterations, or add a real HTTP tool and error handling next?