关于 line-voice-agent
line-voice-agent 是使用 Cartesia Line SDK 编写语音 Agent 的指南,面向构建和部署电话或应用语音交互的开发者。内容涵盖 LLM 配置、工具模式、多 Agent 工作流及部署流程。
主要功能
- 01Python 语音 Agent 开发
- 02LLM 提供方配置
- 03内置通话与搜索工具
- 04自定义工具和 Agent 交接
- 05Cartesia CLI 部署
- 06MCP 服务工具接入
适用场景
- 01构建电话语音 Agent
- 02为语音 Agent 添加 API 或数据库工具
- 03配置多 Agent 通话交接
- 04将 MCP 服务提供给 LLM
MCP 相关
Line SDK 的 mcp_tool 可将远程 HTTP/SSE 或本地 stdio MCP 服务暴露给 LLM,使其列出并调用服务中的工具。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cartesia-ai/ skills --skill line-voice-agent安装后在 Claude Code 中输入/line-voice-agent确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/advanced-patterns.mdcalls-api.mdmulti-agent-workflows.mdtool-patterns.mdtroubleshooting.md详细介绍
Line SDK 语音 Agent 指南
使用 Cartesia Line SDK 构建可用于生产环境的语音 Agent。本指南介绍 Agent 的创建、工具模式、多 Agent 工作流和大模型提供商配置。
Line 的工作方式
Line 是 Cartesia 的语音 Agent 部署平台。你使用 Line SDK 编写 Python Agent 代码,通过 cartesia CLI 将其部署到 Cartesia 的托管云,Cartesia 会托管并自动扩缩容。Cartesia 负责 STT(Ink)、TTS(Sonic)、电话集成和音频编排。每个 Agent 同一时间只能有一个部署处于活动状态;部署完成后,你的 Agent 会自动接听来电。
┌─────────────────────────────────────────────────────────────────┐
│ Cartesia Line Platform │
│ ┌──────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ Ink │───▶│ Your Agent │───▶│ Sonic │ │
│ │ (STT) │ │ (Line SDK) │ │ (TTS) │ │
│ └──────────┘ └──────────────┘ └──────────┘ │
│ ▲ │ │
│ │ Audio Orchestration │ │
│ └────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
▲ │
│ WebSocket ▼
┌───────┴────────────────────────────────────┴───────┐
│ Client (Phone / Web / Mobile) │
└─────────────────────────────────────────────────────┘
你的代码负责:
- 大模型推理和对话流程
- 工具执行(API 调用、数据库查询)
- 多 Agent 协调和交接
Cartesia 负责:
- 语音转文本(Ink)
- 文本转语音(Sonic)
- 实时音频流
- 轮流发言和打断检测
- 部署和自动扩缩容
音频输入选项:
- Cartesia 电话集成 - 托管电话号码
- Calls API - Web 应用、移动应用、自定义电话集成
前提条件
- Python 3.10+ 和 uv(推荐的包管理器)
- Cartesia API 密钥 — 在 play.cartesia.ai/keys 获取(CLI 和部署时使用)
- 大模型 API 密钥 — 用于 Agent 调用的大模型提供商(例如
ANTHROPIC_API_KEY、OPENAI_API_KEY、GEMINI_API_KEY) - Cartesia CLI — 使用以下命令安装:
curl -fsSL https://cartesia.sh | sh
Cartesia CLI 参考
# Authentication
cartesia auth login # Login with Cartesia API key
cartesia auth status # Check auth status
# Project Setup
cartesia create [project-name] # Create project from template
cartesia init # Link existing directory to an agent
# Local Development
cartesia chat <port> # Chat with local agent (text mode)
# Deployment
cartesia deploy # Deploy to Cartesia cloud
cartesia status # Check deployment status
# Environment Variables (encrypted, stored on Cartesia)
cartesia env set KEY=VALUE # Set a single env var
cartesia env set --from .env # Import all vars from .env file
cartesia env rm <name> # Remove an env var
# Agents & Calls
cartesia agents ls # List all agents
cartesia deployments ls # List deployments
cartesia call <phone> [agent-id] # Make outbound call
完整命令参考:docs.cartesia.ai/line/cli。
快速开始
1. 创建项目
cartesia auth login
cartesia create my-agent
cd my-agent
2. 编写 Agent 代码
main.py:
import os
from line.llm_agent import LlmAgent, LlmConfig, end_call
from line.voice_agent_app import AgentEnv, CallRequest, VoiceAgentApp
async def get_agent(env: AgentEnv, call_request: CallRequest):
return LlmAgent(
model="anthropic/claude-haiku-4-5-20251001",
api_key=os.getenv("ANTHROPIC_API_KEY"),
tools=[end_call],
config=LlmConfig(
system_prompt="You are a helpful voice assistant.",
introduction="Hello! How can I help you today?",
),
)
app = VoiceAgentApp(get_agent=get_agent)
if __name__ == "__main__":
app.run()
3. 本地测试
ANTHROPIC_API_KEY=your-key python main.py
cartesia chat 8000 # Text chat with your running agent
4. 部署
cartesia env set ANTHROPIC_API_KEY=your-key # Encrypted, stored on Cartesia
cartesia deploy
cartesia status # Verify deployment is active
5. 拨打电话
cartesia call +1234567890 # Outbound call via CLI
或者从 Cartesia 控制面板触发呼叫。
项目结构
每个 Line Agent 项目都必须包含:
my_agent/
├── main.py # VoiceAgentApp entry point (REQUIRED)
├── cartesia.toml # Deployment config, created by cartesia init or cartesia create (REQUIRED)
└── pyproject.toml # Dependencies: cartesia-line
cartesia.toml 声明部署元数据、本地服务地址,以及 Agent 所需的环境变量:
[cartesia]
name = "My Agent"
description = "What this agent does"
version = "0.1.0"
[cartesia.server]
port = 8000
host = "0.0.0.0"
[cartesia.environment]
required_vars = ["ANTHROPIC_API_KEY"]
核心概念
LlmAgent
通过 LiteLLM 封装大模型提供商的主要 Agent 类:
from line.llm_agent import LlmAgent, LlmConfig
agent = LlmAgent(
model="gemini/gemini-2.5-flash-preview-09-2025", # LiteLLM model string
api_key=os.getenv("GEMINI_API_KEY"), # Provider API key
tools=[end_call, my_custom_tool], # List of tools
config=LlmConfig(...), # Agent configuration
max_tool_iterations=10, # Max tool call loops (default: 10)
backend=None, # Optional provider backend override
)
LlmConfig
用于配置 Agent 行为和大模型采样的选项:
from line.llm_agent import LlmConfig
config = LlmConfig(
# Agent behavior
system_prompt="You are a helpful assistant.",
introduction="Hello! How can I help?", # Set to "" to wait for user first
# Sampling parameters (optional)
temperature=0.7,
max_tokens=1024,
top_p=0.9,
stop=["\n\n"],
seed=42,
presence_penalty=0.0,
frequency_penalty=0.0,
# Reasoning models only: "none" | "minimal" | "low" | "medium" | "high"
reasoning_effort="low",
# Resilience (optional)
num_retries=2, # Default: 2
timeout=30.0,
fallbacks=["gpt-5-nano"], # Fallback models
# Advanced (optional)
strict_tool_schemas=True, # Default: True
extra={}, # Provider-specific pass-through kwargs to LiteLLM
)
reasoning_effort会根据模型进行校验:如果将其传给不支持推理的模型,将引发ValueError。对于非推理模型,请使用"none"(或省略此参数)。
根据 CallRequest 动态配置
使用 LlmConfig.from_call_request() 从传入的呼叫中获取配置:
async def get_agent(env: AgentEnv, call_request: CallRequest):
return LlmAgent(
model="anthropic/claude-sonnet-4-5",
api_key=os.getenv("ANTHROPIC_API_KEY"),
tools=[end_call],
config=LlmConfig.from_call_request(
call_request,
fallback_system_prompt="Default system prompt if not in request.",
fallback_introduction="Default introduction if not in request.",
temperature=0.7, # Additional LlmConfig options
),
)
优先级顺序:CallRequest 值 > 回退参数 > SDK 默认值
VoiceAgentApp
用于管理 HTTP 端点和 WebSocket 连接的应用框架:
from line.voice_agent_app import VoiceAgentApp, AgentEnv, CallRequest
async def get_agent(env: AgentEnv, call_request: CallRequest):
# env.loop - asyncio event loop
# call_request.call_id - unique call identifier
# call_request.agent.system_prompt - from request
# call_request.agent.introduction - from request
# call_request.metadata - custom metadata dict
return LlmAgent(...)
app = VoiceAgentApp(get_agent=get_agent)
app.run(host="0.0.0.0", port=8000)
内置工具
从 line.llm_agent 导入:
from line.llm_agent import (
end_call, send_dtmf, transfer_call, web_search,
knowledge_base, mcp_tool, http_server_tool,
)
end_call
结束当前通话。调用此工具前,请让大模型先向用户道别。
tools=[end_call]
# System prompt: "Say goodbye before ending the call with end_call."
send_dtmf
发送 DTMF 音(电话按键音)。适用于 IVR 导航。
tools=[send_dtmf]
# Buttons: "0"-"9", "*", "#" (strings, not integers!)
transfer_call
转接到另一个电话号码(必须使用 E.164 格式)。
tools=[transfer_call]
# Example: +14155551234
web_search
搜索网络以获取实时信息。如果大模型支持原生网络搜索,则使用该功能;否则回退到 DuckDuckGo。
# Default settings
tools=[web_search]
# Custom settings
tools=[web_search(search_context_size="high")] # "low", "medium", "high"
knowledge_base
通过自然语言查询,从 Agent 的知识库中查找信息。筛选条件、top_k 和 timeout_s 在构造时固定,大模型只能选择查询字符串。
# Default behavior — no filters
tools=[knowledge_base]
# Pre-filter every retrieval, override top_k, or run as a background lookup
tools=[knowledge_base(filters={"category": "billing"}, top_k=10)]
tools=[knowledge_base(description="Look up insurance policy terms.")]
tools=[knowledge_base(is_background=True)]
调用前请告诉用户你正在查找信息 — 检索可能需要一些时间。失败时会引发 KnowledgeBaseError(从 line 导入)。
mcp_tool
向大模型开放一个 Model Context Protocol 服务。需要 Python 3.10+ 和 mcp 包(Line 已将其作为依赖)。
# Remote HTTP/SSE server
tools=[mcp_tool(name="dmcp", server_url="https://dmcp-server.deno.dev/sse")]
# Local stdio server
tools=[mcp_tool(name="memory", command="npx -y @modelcontextprotocol/server-memory")]
大模型调用该工具时,不传参数即可列出可用工具;也可以传入 tool_name 和 tool_args 来调用工具。
http_server_tool
根据 JSON 架构创建 HTTP/webhook 工具——无需自定义函数。LLM
会填写架构字段,SDK 会发起请求。带有
constant_value 的属性会对 LLM 隐藏,并注入每个请求;
auth 中的 ${ENV_VAR} 占位符会在构建时从 os.environ 中解析。
create_ticket = http_server_tool(
name="create_ticket",
description="Creates a support ticket for the caller.",
url="https://api.example.com/v1/{tenant_id}/tickets", # {param} = path variable
method="POST",
request_body_schema={
"type": "object",
"required": ["subject", "priority"],
"properties": {
"subject": {"type": "string", "description": "Short summary."},
"priority": {"type": "string", "enum": ["low", "medium", "high"]},
"source": {"type": "string", "constant_value": "voice_agent"}, # hidden
},
},
query_params_schema=None, # same shape, scalar types only, for GET query params
auth={"Authorization": "Bearer ${SUPPORT_API_KEY}"},
content_type="application/json", # or "application/x-www-form-urlencoded"
timeout=5.0,
is_background=True, # default True
)
tools=[create_ticket, end_call]
LLM 始终会收到结构化的 JSON 结果,例如
{"ok": true, "status": 201, "body": "..."} 或 {"ok": false, "status": 500, "error": "..."}。
注意:部分 Line 文档/README 将其称为
webhook_tool;导出的 函数名是http_server_tool。
自定义工具类型
适用于不同使用场景的三种工具范式:
| 类型 | 装饰器 | 使用场景 | 结果处理 |
|---|---|---|---|
| Loopback | @loopback_tool | API 调用、数据库查询 | 将结果返回给 LLM |
| Passthrough | @passthrough_tool | 结束通话、转接、DTMF | 绕过 LLM,直接发送给用户 |
| Handoff | @handoff_tool | 多 Agent 工作流 | 将控制权转交给另一个 Agent |
工具类型决策树
Does the result need LLM processing?
├─ YES → @loopback_tool
│ └─ Is it long-running (>1s)? → @loopback_tool(is_background=True)
│ └─ Yield interim status, then final result
├─ NO, deterministic action → @passthrough_tool
│ └─ Yields OutputEvent objects directly (AgentSendText, AgentEndCall, etc.)
└─ Transfer to another agent → @handoff_tool or agent_as_handoff()
Loopback 工具
将结果返回给 LLM,以指导下一条回复:
from typing import Annotated
from line.llm_agent import loopback_tool, ToolEnv
@loopback_tool
async def get_order_status(
ctx: ToolEnv,
order_id: Annotated[str, "The order ID to look up"],
) -> str:
"""Look up the current status of an order."""
order = await db.get_order(order_id)
return f"Order {order_id} status: {order.status}, ETA: {order.eta}"
参数语法:
- 第一个参数必须是
ctx: ToolEnv - 对 LLM 可见的参数使用
Annotated[type, "description"] - 工具描述来自文档字符串
- 可选参数需要默认值(不能仅使用
Optional[T])
@loopback_tool
async def search_products(
ctx: ToolEnv,
query: Annotated[str, "Search query"],
category: Annotated[str, "Product category"] = "all", # Optional with default
limit: Annotated[int, "Max results"] = 10,
) -> str:
"""Search the product catalog."""
...
Passthrough 工具
结果绕过 LLM,直接发送给用户/系统:
from line.events import AgentSendText, AgentTransferCall
from line.llm_agent import passthrough_tool, ToolEnv
@passthrough_tool
async def transfer_to_support(
ctx: ToolEnv,
reason: Annotated[str, "Reason for transfer"],
):
"""Transfer the call to the support team."""
yield AgentSendText(text="Let me transfer you to our support team now.")
yield AgentTransferCall(target_phone_number="+18005551234")
输出事件类型(来自 line.events):
AgentSendText(text="...")- 向用户播报文本AgentEndCall()- 结束通话AgentTransferCall(target_phone_number="+1...")- 转接通话AgentSendDtmf(button="5")- 发送 DTMF 音
Handoff 工具
将控制权转交给另一个 Agent。请参阅多 Agent 工作流。
上下文管理
LlmAgent 提供一个 history 对象,用于注入和转换 LLM 所见的对话历史。
agent = LlmAgent(model="gemini/gemini-2.5-flash-preview-09-2025", api_key=...)
# Inject a custom entry (defaults to role="user"; pass role="system" for a system note)
agent.history.add_entry("The customer's name is Alice and she has a premium account.")
# Anchor an insertion relative to an existing event
agent.history.add_entry("Reminder: stay concise.", role="system", after=some_event)
# Replace a segment of history with new events (filtering, summarization, etc.)
agent.history.update(new_events, start=first_event, end=last_event)
条目会延迟插入,并在多个轮次间保留。在工具内部,可以调用
agent.history.add_entry(...),持久保存从外部 API 获取的丰富上下文。
注意:部分 Line README 使用
agent.add_history_entry(...)/agent.set_history_processor(...)。实际实现的 API 是agent.history.add_entry(...)和agent.history.update(...)。
模型选择策略
主对话 Agent 使用 FAST 模型:
gemini/gemini-2.5-flash-preview-09-2025(推荐)anthropic/claude-haiku-4-5-20251001gpt-5-nano
仅通过后台工具调用使用 POWERFUL 模型,处理复杂推理:
anthropic/claude-opus-4-5gpt-5.2
这种模式既能让对话保持响应迅速,又能在需要时使用深度推理。实现方法请参阅高级模式中的双层 Agent 模式。
LLM 提供商
Line SDK 使用 LiteLLM 模型字符串。常见格式:
| 提供商 | 格式 | 示例 |
|---|---|---|
| OpenAI | model_name | gpt-5.2、gpt-5-nano |
| Anthropic | anthropic/model_name | anthropic/claude-sonnet-4-5、anthropic/claude-haiku-4-5-20251001 |
| Google Gemini | gemini/model_name | gemini/gemini-2.5-flash-preview-09-2025 |
| Azure OpenAI | azure/deployment_name | azure/my-deployment |
设置相应的 API 密钥环境变量:
OPENAI_API_KEYANTHROPIC_API_KEYGEMINI_API_KEYAZURE_API_KEY
完整列表:https://docs.litellm.ai/docs/providers
常见模式
使用自定义工具的 Agent
from typing import Annotated
from line.llm_agent import LlmAgent, LlmConfig, loopback_tool, end_call, ToolEnv
@loopback_tool
async def check_appointment(
ctx: ToolEnv,
date: Annotated[str, "Date in YYYY-MM-DD format"],
) -> str:
"""Check available appointment slots for a given date."""
slots = await calendar.get_available_slots(date)
return f"Available slots on {date}: {', '.join(slots)}"
@loopback_tool
async def book_appointment(
ctx: ToolEnv,
date: Annotated[str, "Date in YYYY-MM-DD format"],
time: Annotated[str, "Time in HH:MM format"],
name: Annotated[str, "Customer name"],
) -> str:
"""Book an appointment slot."""
result = await calendar.book(date, time, name)
return f"Appointment booked for {name} on {date} at {time}. Confirmation: {result.id}"
async def get_agent(env: AgentEnv, call_request: CallRequest):
return LlmAgent(
model="gemini/gemini-2.5-flash-preview-09-2025",
api_key=os.getenv("GEMINI_API_KEY"),
tools=[check_appointment, book_appointment, end_call],
config=LlmConfig(
system_prompt="""You are an appointment scheduling assistant.
Help users check availability and book appointments.
Always confirm the booking details before finalizing.""",
introduction="Hi! I can help you schedule an appointment. What date works for you?",
),
)
等待用户先开口
将 introduction="" 设置为让 Agent 等待用户发言:
config=LlmConfig(
system_prompt="You are a helpful assistant.",
introduction="", # Empty string = wait for user
)
表单填写模式
请参阅表单填写示例,了解如何通过语音收集结构化数据。关键模式:
@loopback_tool
async def record_answer(
ctx: ToolEnv,
answer: Annotated[str, "The user's answer"],
) -> dict:
"""Record an answer to the current question."""
# Process and validate answer
# Return next question or completion status
return {"next_question": "What is your email?", "is_complete": False}
常见错误及避免方法
-
缺少
end_call工具 - 如果未包含该工具(或类似的自定义工具),Agent 就无法自行结束通话,只能等待用户挂断 -
在工具中引发异常 - 返回便于用户理解的错误字符串:
# BAD raise ValueError("Invalid order ID") # GOOD return "I couldn't find that order. Please check the ID and try again." -
忘记
ctx参数 - 第一个参数必须是ctx: ToolEnv:# GOOD @loopback_tool async def my_tool(ctx: ToolEnv, order_id: Annotated[str, "Order ID"]): ... -
在交接工具中忘记
event- 交接工具必须包含event参数:# GOOD @handoff_tool async def my_handoff(ctx: ToolEnv, param: Annotated[str, "desc"], event): ... -
缺少 Annotated 描述 - 大模型需要参数描述:
# GOOD async def my_tool(ctx, order_id: Annotated[str, "The order ID to look up"]): ... -
阻塞长时间运行的操作 - 使用
is_background=True并提供中间状态:@loopback_tool(is_background=True) async def slow_search(ctx: ToolEnv, query: Annotated[str, "Query"]): yield "Searching..." # Immediate feedback result = await slow_operation() yield result -
直接使用同步 API - 使用
asyncio.to_thread()包装同步调用:result = await asyncio.to_thread(sync_api_call, params) -
在主对话中使用速度较慢的模型 - 主 Agent 使用速度较快的模型(haiku、flash、mini),仅通过后台工具使用能力强大的模型。
参考文档
在此技能中:
- 工具模式 - 深入了解工具实现
- 多 Agent 工作流 - 交接、包装器、护栏
- 高级模式 - 后台工具、状态、事件
- Calls API - Web 和移动应用的 WebSocket 集成
- 故障排除 - 常见问题和调试
在 docs.cartesia.ai 上:
- SDK 概览 — 架构和安装
- 工具指南 — 深入了解工具类型
- Agent 指南 — LlmAgent、自定义 Agent、对话循环
- 事件参考 — 输入/输出事件
- CLI 参考 — 部署、环境、Agent、通话
关键导入
# Core
from line.llm_agent import LlmAgent, LlmConfig
from line.voice_agent_app import VoiceAgentApp, AgentEnv, CallRequest
# Built-in tools
from line.llm_agent import (
end_call, send_dtmf, transfer_call, web_search,
knowledge_base, mcp_tool, http_server_tool,
)
# Tool decorators
from line.llm_agent import loopback_tool, passthrough_tool, handoff_tool
# Tool context
from line.llm_agent import ToolEnv
# Multi-agent
from line.llm_agent import agent_as_handoff
# Knowledge base (errors / client)
from line import KnowledgeBase, KnowledgeBaseError
# Events (for passthrough/handoff tools and custom agents)
from line.events import (
AgentSendText,
AgentEndCall,
AgentTransferCall,
AgentSendDtmf,
AgentUpdateCall,
AgentSendCustom,
CustomHistoryEntry,
HistoryEvent,
)
关键参考文件
实现 Line SDK Agent 时,请参考 cartesia-ai/line 仓库中的以下示例文件:
examples/basic_chat/main.py- 最简单的 Agent 模式(web_search)examples/form_filler/- 带状态的回环工具examples/chat_supervisor/main.py- 使用双层模型策略的后台工具examples/transfer_agent/main.py- 多 Agent 交接examples/transfer_phone_call/main.py- IVR 导航和电话转接examples/guardrails_wrapper/- 为 Agent 添加护栏包装examples/sales_with_leads/- 有状态的潜在客户信息提取与调研examples/echo/tools.py- 自定义交接工具example_integrations/- Exa、Tavily、Cerebras、Browserbase 集成
相关 Cartesia 技能
对于直接 HTTP/WebSocket 集成(在自有后端中使用 TTS/STT/语音、SDK、可选 MCP)——而非 Line 部署,请使用此仓库中的 cartesia-api。