connect-agent

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Connect Your Agent to LangWatch Simulations

将你的 Agent 连接到 LangWatch 模拟

Connect the agent in this codebase to LangWatch, so test suites run from the platform against the real agent process. The SDK opens an outbound connection to LangWatch from the process that already runs the agent, registers the agent with its environment and its run parameters, and receives one call per conversation turn. The simulation exercises the real code, dependencies, secrets and traces.
Three steps: install the SDK, add the connect function where the service starts, start the service the way the user always starts it. Work through them in order, confirm the agent reads Online, run one test suite, then report what changed and the result.
In Langy, do not print the change for the user to apply by hand: call
code_access
and follow the
code-changes
skill to make it on their machine or through GitHub.
Use the HTTP fallback at the bottom of this skill ONLY when the agent cannot import the SDK: the agent is written in a language with no LangWatch SDK, or you have no access to its code.
将此代码库中的 Agent 连接到 LangWatch,使测试套件能够从平台针对真实 Agent 进程运行。SDK 从已经运行 Agent 的进程发起一条指向 LangWatch 的出站连接,登记 Agent 及其环境和运行参数,并为每个对话轮次接收一次调用。模拟会实际执行真实代码,并使用真实的依赖项、secrets 和 traces。
三个步骤:安装 SDK、在服务启动位置添加 connect 函数、按用户一贯的方式启动服务。依次完成这些步骤,确认 Agent 显示为 Online,运行一个测试套件,然后报告变更内容和结果。
在 Langy 中,不要打印变更内容让用户手动应用:应调用
code_access
并遵循
code-changes
skill,在用户的机器上或通过 GitHub 完成变更。
仅当 Agent 无法导入 SDK 时,才使用本 skill 底部的 HTTP fallback:即 Agent 所用语言没有 LangWatch SDK,或者无法访问其代码。

Step 1: Set up the LangWatch CLI

Step 1:设置 LangWatch CLI

Use
langwatch docs <path>
to read documentation as Markdown. Some useful entry points:
bash
langwatch docs                                    # Docs index
langwatch docs integration/python/guide           # Python integration
langwatch docs integration/typescript/guide       # TypeScript integration
langwatch docs prompt-management/cli              # Prompts CLI
langwatch scenario-docs                           # Scenario docs index
Discover commands with
langwatch --help
and
langwatch <subcommand> --help
. List and get commands accept
--format json
for machine-readable output. Every list command takes
--limit <n>
to cap the rows and
--jq <expr>
to read part of the answer. A paginated list answers with an envelope, so count its rows through the row array (
--jq '.traces | length'
), and read how many there are in all at
.pagination.total
. Bare
--jq length
counts the fields of the envelope, not the rows. Read the docs first instead of guessing SDK APIs or CLI flags.
If no shell is available, fetch the same Markdown over plain HTTP. Append
.md
to any docs path (e.g. https://langwatch.ai/docs/integration/python/guide.md). Index: https://langwatch.ai/docs/llms.txt. Scenario index: https://langwatch.ai/scenario/llms.txt
If anything fails or confuses you while following this skill (broken commands, docs that do not match reality, errors you had to work around), ask the user for permission and run
npx --yes langwatch report --user-approved
with a
--title
and
--summary
(or
--session <transcript.jsonl>
) to send it to the LangWatch team, and it directly shapes what gets fixed. No login or API key needed. Nothing is sent without
--user-approved
, and
--dry-run
prints the exact payload without sending anything. The title, summary and transcript are scrubbed locally first, by pattern: secrets and API keys, plus email addresses, phone numbers, card numbers and public IPv4 addresses. Anything no pattern matches is sent as written, including a contact address passed with
--email
. With
--session
, always run
--dry-run
first and let the user read the payload, because a transcript carries content they never reviewed.
npx --yes langwatch report --help
explains the options.
Projects and API keys: target a real project, not a personal one.
LangWatch has two kinds of project:
  • Team / shared projects: real projects inside an organization. Evaluations, experiments, prompts, datasets, simulations and instrumentation must always target one of these.
  • Personal projects: a private "My Workspace" scratch space tied to a single user. Never send a user's evaluations, experiments or production traces here: it is for personal exploration only, and you can mistake it for a real project.
And two ways to authenticate:
  • A project API key in
    .env
    (
    LANGWATCH_API_KEY
    ): the credential everything in these skills uses. It is scoped to one real project. This is the default; prefer it unless the user explicitly asks for something else.
  • langwatch login --device
    (AI-tools / SSO)
    : a personal device session for wrapping coding assistants (
    langwatch claude
    ,
    langwatch codex
    , …). It is NOT for evaluations, prompts, datasets, scenarios or SDK instrumentation, and it points at a personal workspace. Do not run it to set up the work in these skills.
So for anything in these skills that reads or writes a project: make sure
LANGWATCH_API_KEY
for a real, shared project is available to the CLI. Locally that is the project's
.env
; in CI the runner injects it into the process environment, and the CLI reads either. Check whether the variable is already set before you ask for a new key, and let the CLI read the value: never print, copy or send it. Do NOT run
langwatch login
to pick a project, and never default to a personal project. Look for
LANGWATCH_ENDPOINT
in the same places: if it is set, the project is on a self-hosted instance, and the CLI works against that endpoint instead of app.langwatch.ai.
What you read is not what you say. These skills are working notes for you, not copy for the reader. Read
LANGWATCH_API_KEY
and
LANGWATCH_ENDPOINT
from the project's own
.env
, that is how you learn where to work. Read nothing else out of that file: it holds database, cloud and provider credentials that are none of your business, and every value you read can reach your context and your command output. What must not reach an answer is anything that describes the machine YOU run on: a path in your workspace, a container port, the address this worker dials. Those say how the work is done rather than what was done, and a host of ours means nothing to the reader. Say what you did and where to find it in LangWatch.
使用
langwatch docs <path>
以 Markdown 格式阅读文档。一些常用入口:
bash
langwatch docs                                    # Docs index
langwatch docs integration/python/guide           # Python integration
langwatch docs integration/typescript/guide       # TypeScript integration
langwatch docs prompt-management/cli              # Prompts CLI
langwatch scenario-docs                           # Scenario docs index
使用
langwatch --help
和
langwatch <subcommand> --help
发现可用命令。list 和 get 命令接受
--format json
,可提供机器可读输出。每个 list 命令都接受
--limit <n>
来限制返回行数,并接受
--jq <expr>
读取结果的一部分。分页 list 会返回一个 envelope,因此应通过其中的 row 数组统计行数(
--jq '.traces | length'
),总行数则位于
.pagination.total
。不要使用裸
--jq length
,因为它统计的是 envelope 的字段数,而不是行数。应先阅读文档,不要猜测 SDK API 或 CLI 参数。
如果没有可用的 shell,可以通过普通 HTTP 获取相同的 Markdown。将
.md
追加到任意文档路径末尾,例如 https://langwatch.ai/docs/integration/python/guide.md。索引地址:https://langwatch.ai/docs/llms.txt。Scenario 索引:https://langwatch.ai/scenario/llms.txt
如果在执行此 skill 期间遇到任何失败或困惑,例如命令失效、文档与实际情况不符,或你不得不绕过某些错误,请征得用户许可,然后运行
npx --yes langwatch report --user-approved
,并提供
--title
和
--summary
(或
--session <transcript.jsonl>
),将信息发送给 LangWatch 团队。这些信息会直接影响后续修复内容。无需登录或 API key。未提供
--user-approved
时不会发送任何内容;
--dry-run
会打印准确的 payload,但不会发送。标题、摘要和 transcript 会先在本地按照模式进行脱敏,包括 secrets、API keys、email 地址、电话号码、卡号和公开 IPv4 地址。任何未被模式匹配的内容都会原样发送,包括通过
--email
传入的联系地址。使用
--session
时,务必先运行
--dry-run
并让用户阅读 payload,因为 transcript 可能包含用户从未审阅过的内容。
npx --yes langwatch report --help
会说明相关选项。
项目和 API keys:应指向真实项目,而不是个人项目。
LangWatch 有两类项目:
  • 团队/共享项目:组织内的真实项目。评估、实验、prompts、datasets、simulations 和 instrumentation 必须始终指向其中之一。
  • 个人项目:与单个用户绑定的私有“My Workspace”临时空间。绝不能将用户的评估、实验或生产 traces 发送到这里;它仅用于个人探索,很容易误将它当成真实项目。
认证方式也有两种:
  • .env
    中的项目 API key
    (
    LANGWATCH_API_KEY
    ):这些 skills 使用的凭据,作用域限定为一个真实项目。这是默认方式,除非用户明确要求其他方式,否则应优先使用。
  • langwatch login --device
    (AI-tools / SSO)
    :用于包装 coding assistants 的个人设备会话(
    langwatch claude
    、
    langwatch codex
    等)。它不适用于评估、prompts、datasets、scenarios 或 SDK instrumentation,并且指向个人 workspace。不要运行它来设置这些 skills 中的工作。
因此,对于这些 skills 中任何读取或写入项目的操作,请确保 CLI 可以访问真实共享项目的
LANGWATCH_API_KEY
。在本地,该 key 位于项目的
.env
中;在 CI 中,由 runner 注入进程环境,CLI 会从任一位置读取。检查变量是否已设置后再向用户索取新 key,并让 CLI 自行读取其值:绝不要打印、复制或发送它。不要运行
langwatch login
来选择项目,也绝不能默认使用个人项目。在相同位置查找
LANGWATCH_ENDPOINT
:如果已设置,说明项目位于自托管实例上,CLI 将使用该 endpoint,而不是 app.langwatch.ai。
你读到的内容不应被直接复述。
这些 skills 是工作笔记,不是写给读者看的文案。从项目自己的
.env
中读取
LANGWATCH_API_KEY
和
LANGWATCH_ENDPOINT
,由此确定工作位置。不要读取该文件中的其他内容:其中还包含数据库、云服务和 provider 凭据,这些都与你无关;而你读取的每个值都可能进入上下文和命令输出。任何描述你所运行机器的信息都不能出现在回答中,例如 workspace 中的路径、容器端口或当前 worker 拨打的地址。这些信息描述的是工作执行方式,而不是完成的工作,对读者也没有帮助。你应当说明做了什么,以及可以在 LangWatch 的哪里找到它。

Step 2: Install the SDK

Step 2:安装 SDK

Read the codebase first. Find two things: the entry point that runs the agent, which is what the connect function calls, and the file that starts the service, which is where the connect function goes.
bash
pip install langwatch          # Python
npm install langwatch zod      # TypeScript, Node only
The process needs
LANGWATCH_API_KEY
in its environment. It is the same project key the CLI uses. Without a key the SDK logs one line and opens no connection.
先阅读代码库。找到两个关键位置:运行 Agent 的入口点,也就是 connect 函数要调用的位置;以及启动服务的文件,也就是 connect 函数应放置的位置。
bash
pip install langwatch          # Python
npm install langwatch zod      # TypeScript, Node only
进程的环境变量中必须包含
LANGWATCH_API_KEY
。它与 CLI 使用的是同一个项目 key。没有 key 时,SDK 会记录一行日志,并且不会建立连接。

Step 3: Add the connect function where the service starts

Step 3:在服务启动位置添加 connect 函数

Write a small function beside the code that starts the service. It is the adapter between the messages that come from a scenario test and the agent in the codebase.
Put it in the file that starts the service, or in a module that file imports at startup. The connection opens with the process, so the code has to run when the process runs.
Python
python
undefined
在启动服务的代码旁编写一个小型函数。它是 scenario test 传入的消息与代码库中 Agent 之间的适配器。
将它放在启动服务的文件中,或放在该文件启动时导入的模块中。连接会随进程一起打开,因此代码必须在进程运行时执行。
Python
python
undefined

main.py, beside the server startup

main.py, beside the server startup

import langwatch
from my_app.agent import SupportAgent
@langwatch.connect_agent(name="support-agent") async def support_agent( messages: list[dict], thread_id: str, *, model: str = "gpt-5-mini", ) -> str: result = await SupportAgent(model=model).run(messages, conversation_id=thread_id) return result.output_text

**TypeScript**

```typescript
// server.ts, beside the server startup
import { z } from "zod";
import { connectAgent } from "langwatch/agent";

import { runSupportAgent } from "./agent";

connectAgent(
  {
    name: "support-agent",
    parameters: z.object({
      model: z.enum(["gpt-5", "gpt-5-mini"]).default("gpt-5-mini"),
    }),
  },
  async ({ messages, threadId, params }) => {
    const result = await runSupportAgent({
      messages,
      conversationId: threadId,
      model: params.model,
    });
    return result.text;
  },
);
Rules for the connect function:
  • name
    is required. Use the name the team calls the agent, in lower case with dashes.
  • Call the agent the product already runs. Do not reimplement it in the connect function, and do not call a simplified copy of it, or the suite tests code no customer reaches.
  • Change nothing about how the service starts. The connect function runs on the startup path, and the start command stays the same.
  • Turn fields are what the platform sends on every call:
    messages
    (the whole conversation, OpenAI-style),
    new_messages
    (
    newMessages
    , the delta since the last turn),
    thread_id
    (
    threadId
    ),
    session
    ,
    trace_id
    (
    traceId
    ). In Python, declare only the ones you use and the SDK passes exactly those. In TypeScript, they arrive as one object, so destructure what you need.
  • Return a string, one message, a list of messages, or
    langwatch.AgentReply(output, session=...)
    (
    { output, session }
    in TypeScript).
  • The connect function is the adapter, and only it carries the decorator. Never place the decorator on a function the app already has when that function returns its own result, for example a dict with the output, a thread id and an order number: the SDK cannot turn that into a reply, and every turn of the run times out. Call that function from the connect function and return its reply text.
  • Do not change the agent's own code to fit the connect function. Map the turn onto the agent's existing call in the connect function instead.
  • Do NOT add a
    traceparent
    middleware. The SDK adopts the turn's trace context before it calls the function, so the agent's spans land in the turn's trace and the judge reads them.
import langwatch
from my_app.agent import SupportAgent
@langwatch.connect_agent(name="support-agent") async def support_agent( messages: list[dict], thread_id: str, *, model: str = "gpt-5-mini", ) -> str: result = await SupportAgent(model=model).run(messages, conversation_id=thread_id) return result.output_text

**TypeScript**

```typescript
// server.ts, beside the server startup
import { z } from "zod";
import { connectAgent } from "langwatch/agent";

import { runSupportAgent } from "./agent";

connectAgent(
  {
    name: "support-agent",
    parameters: z.object({
      model: z.enum(["gpt-5", "gpt-5-mini"]).default("gpt-5-mini"),
    }),
  },
  async ({ messages, threadId, params }) => {
    const result = await runSupportAgent({
      messages,
      conversationId: threadId,
      model: params.model,
    });
    return result.text;
  },
);
connect 函数规则:
  • name
    为必填项。使用团队对 Agent 的称呼,并采用小写加短横线的形式。
  • 调用产品已经运行的 Agent。不要在 connect 函数中重新实现它,也不要调用它的简化副本,否则测试套件测试到的将不是任何客户会走到的代码路径。
  • 不要改变服务的启动方式。connect 函数在启动路径上运行,启动命令保持不变。
  • Turn fields 是平台在每次调用时传入的字段:
    messages
    (完整对话,采用 OpenAI 风格)、
    new_messages
    (
    newMessages
    ,表示自上一轮以来的增量)、
    thread_id
    (
    threadId
    )、
    session
    、
    trace_id
    (
    traceId
    )。在 Python 中,只声明你使用的字段,SDK 会恰好传递这些字段。在 TypeScript 中,它们以单个对象的形式到达,因此应解构所需字段。
  • 可以返回一个字符串、一条消息、消息列表,或
    langwatch.AgentReply(output, session=...)
    (在 TypeScript 中为
    { output, session }
    )。
  • connect 函数是适配器,只有它可以携带 decorator。不要将 decorator 放在应用已有且会返回自身结果对象的函数上,例如返回包含 output、thread id 和 order number 的 dict:SDK 无法将该对象转换为 reply,并且运行中的每一轮都会超时。应在 connect 函数中调用那个函数,并返回它的 reply 文本。
  • 不要为了适配 connect 函数而修改 Agent 自身的代码。应在 connect 函数中将 turn 映射到 Agent 已有的调用方式。
  • 不要添加
    traceparent
    middleware。SDK 会在调用函数前采用该 turn 的 trace context,因此 Agent 的 spans 会落在该 turn 的 trace 中,judge 也能读取它们。

Run parameters

运行参数

A Python parameter that is not a turn field is a run parameter. The platform offers it in the run dialog and in
--param
. A parameter with a default is optional, and the run dialog prefills that default. A parameter with no default is required, and a run that does not supply a value for it is refused before the function is called.
python
from typing import Literal

@langwatch.connect_agent(name="support-agent")
async def support_agent(
    messages: list[dict],
    *,
    model: Literal["gpt-5", "gpt-5-mini"] = "gpt-5-mini",
    plan: str = "free",
    max_tools: int = 5,
) -> str:
    ...
TypeScript declares them with a zod schema in
parameters
, the way Step 3 shows. Read the values out of
params
in the connect function and pass them into the agent's own call. The schema types
params
in the handler, and the SDK validates the values a run supplies against it. Add
zod
to the project (
npm install zod
) if it is not there yet. Read the schema this way:
  • z.enum([...])
    becomes a closed option list in the run dialog. A value outside the list is refused.
  • .default(value)
    sets the default.
  • .describe(text)
    sets the description shown beside the field.
  • z.number()
    and
    z.boolean()
    set the parameter type.
    z.string()
    is text.
Give every property a default, or the run must supply a value for it. Keep the schema flat and scalar: nested objects and arrays are not run parameters.
valibot and arktype work the same way, and so does any other Standard Schema object that has a JSON Schema converter.
parameters
also takes a definition map for a project with no schema library (
{ model: { options: ["gpt-5", "gpt-5-mini"], default: "gpt-5-mini" } }
), or a plain JSON Schema object.
Declare a parameter for a value the tests must vary: a model, a plan, a tenant, a fixture id.
Literal
and
Enum
in Python, and
z.enum
in TypeScript, become a closed list that the run dialog offers as choices and the platform refuses a value outside of.
在 Python 中,不属于 turn field 的参数就是 run parameter。平台会在 run dialog 和
--param
中提供它。带默认值的参数是可选的,run dialog 会预先填入该默认值。没有默认值的参数是必填项,未提供相应值的 run 会在调用函数前被拒绝。
python
from typing import Literal

@langwatch.connect_agent(name="support-agent")
async def support_agent(
    messages: list[dict],
    *,
    model: Literal["gpt-5", "gpt-5-mini"] = "gpt-5-mini",
    plan: str = "free",
    max_tools: int = 5,
) -> str:
    ...
TypeScript 使用
parameters
中的 zod schema 声明运行参数,方式如 Step 3 所示。在 connect 函数中从
params
读取这些值,并传给 Agent 自己的调用。schema 负责为 handler 中的
params
提供类型,SDK 会根据 schema 校验每次 run 提供的值。如果项目中还没有 zod,请添加它(
npm install zod
)。按以下方式理解 schema:
  • z.enum([...])
    会在 run dialog 中变成封闭选项列表。列表之外的值会被拒绝。
  • .default(value)
    设置默认值。
  • .describe(text)
    设置字段旁显示的说明。
  • z.number()
    和
    z.boolean()
    设置参数类型。
    z.string()
    表示文本。
每个属性都应提供默认值,否则每次 run 都必须提供相应值。保持 schema 为扁平的标量结构:嵌套对象和数组不能作为 run parameter。
valibot、arktype,以及任何其他带有 JSON Schema converter 的 Standard Schema 对象,其工作方式也相同。对于没有 schema library 的项目,
parameters
还可以接受 definition map(
{ model: { options: ["gpt-5", "gpt-5-mini"], default: "gpt-5-mini" } }
),或者普通的 JSON Schema 对象。
如果测试需要改变某个值,就为它声明参数,例如 model、plan、tenant 或 fixture id。Python 中的
Literal
和
Enum
,以及 TypeScript 中的
z.enum
,会变成 run dialog 可选择的封闭列表,平台也会拒绝列表之外的值。

Session, when the agent mints its own conversation id

当 Agent 自己创建 conversation id 时的 Session

thread_id
is the platform's conversation id.
session
is the agent's own memory of the conversation: any JSON value,
None
on the first turn, and whatever the function returns comes back on the next turn of the same conversation.
python
@langwatch.connect_agent(name="support-agent")
async def support_agent(messages: list[dict], session: str | None = None):
    conversation_id = session or await my_agent.create_conversation()
    reply = await my_agent.send(conversation_id, messages[-1]["content"])
    return langwatch.AgentReply(reply, session=conversation_id)
The platform holds the value for the run, so this works with any number of production instances. An in-memory map keyed by
thread_id
works too, with
sticky=True
on the decorator to keep a conversation on one instance.
Use
session
when the agent's API creates its own conversation and cannot accept an id from outside. An agent that reads the whole
messages
list on every turn needs nothing here.
thread_id
是平台的 conversation id。
session
是 Agent 对 conversation 自己的记忆:它可以是任意 JSON 值,第一轮为
None
,函数返回的内容会在同一 conversation 的下一轮再次传回。
python
@langwatch.connect_agent(name="support-agent")
async def support_agent(messages: list[dict], session: str | None = None):
    conversation_id = session or await my_agent.create_conversation()
    reply = await my_agent.send(conversation_id, messages[-1]["content"])
    return langwatch.AgentReply(reply, session=conversation_id)
平台会为整个 run 保存该值,因此无论运行多少个生产实例都可以正常工作。也可以使用以
thread_id
为键的内存 map,并在 decorator 上设置
sticky=True
,让同一 conversation 始终留在同一个实例上。
当 Agent 的 API 会创建自己的 conversation,并且无法接受外部传入的 id 时,应使用
session
。如果 Agent 每一轮都会读取完整的
messages
列表,则无需在此处理。

Environment

Environment

The SDK reads the environment from the
environment
argument, then
LANGWATCH_AGENT_ENVIRONMENT
, then
APP_ENV
,
ENVIRONMENT
and
NODE_ENV
, and falls back to
development
. Each environment is a separate row on the agents page, so production and a developer machine are two targets that a comparison run puts side by side.
development
makes the agent personal: only its owner can run it when the key is personal, and only that machine registers it when the key is a project key. Name the environment
dev-shared
for a development box the whole team runs against. On a project key the machine is its hostname, so a container whose hostname changes on every recreate registers a new row each start; give it an environment name or pin the hostname (
hostname:
in Compose,
--hostname
on
docker run
).
Leave
environment
out when the service already sets
LANGWATCH_AGENT_ENVIRONMENT
,
APP_ENV
,
ENVIRONMENT
or
NODE_ENV
. The SDK reads them on its own.
SDK 会先从
environment
参数读取 environment,然后依次读取
LANGWATCH_AGENT_ENVIRONMENT
、
APP_ENV
、
ENVIRONMENT
和
NODE_ENV
,最后回退到
development
。每个 environment 在 agents 页面中对应一条独立记录,因此 production 和开发机器会成为两个独立目标,比较 run 可以将它们并排展示。
development
会使 Agent 成为个人 Agent:当使用个人 key 时,只有其所有者可以运行它;当使用项目 key 时,只有该机器会登记它。对于整个团队共同使用的开发环境,应将 environment 命名为
dev-shared
。使用项目 key 时,机器标识就是其 hostname;如果容器每次重新创建时 hostname 都会变化,每次启动就会登记一条新记录;因此应指定 environment 名称,或固定 hostname(Compose 中使用
hostname:
,
docker run
中使用
--hostname
)。
如果服务已经设置
LANGWATCH_AGENT_ENVIRONMENT
、
APP_ENV
、
ENVIRONMENT
或
NODE_ENV
,则不要传入
environment
。SDK 会自行读取这些变量。

Step 4: Start the service and confirm the agent is Online

Step 4:启动服务并确认 Agent 为 Online

Start the service the way the user starts it. Do not add a start command, and do not write a separate runner script: the connect function is already on the startup path, and a web server holds the connection open by itself.
The exception is an agent that is a library with no process of its own. Put the connect function in a small script and keep the script alive:
python
langwatch.agent.serve()   # Python, at the bottom of the script
typescript
// TypeScript: keep the script alive yourself. The WebSocket holds the event loop
// while connected, but the HTTP fallback and the reconnect wait do not.
setInterval(() => {}, 1 << 30);
// Run it with the project's TypeScript runner, for example `tsx agent.ts`
Then confirm from the CLI, in another terminal:
bash
langwatch agent list
The row for the agent reads
Online
with the environment and the instance count.
Offline
means the process is not connected: check that
LANGWATCH_API_KEY
is set in the process environment, that the process is running, and that outbound WSS to the LangWatch endpoint is allowed. When the network refuses the WebSocket upgrade, the SDK falls back to HTTP long polling on its own;
LANGWATCH_AGENT_TRANSPORT=http
forces that transport from the start.
Then read back what the platform registered:
bash
langwatch agent get <agent-id> --format json
The answer carries
environment
,
status
,
instances
(hostname, pid, SDK and version per process) and
parameters
: one entry per run parameter with
name
,
type
,
options
(the closed list from
Literal
,
Enum
or
z.enum
),
defaultValue
and
description
. Compare it with the signature. A parameter that is missing was read as a turn field; one with no default is still registered, as a required parameter every run has to supply. An option list that is missing means the annotation was not a
Literal
, an
Enum
or a
z.enum
. Fix the code and restart the process; the row updates on the next registration.
按照用户一贯的方式启动服务。不要添加新的启动命令,也不要编写单独的 runner script:connect 函数已经位于启动路径上,而 Web server 本身会保持连接。
唯一的例外是没有自身进程的 Agent library。此时应将 connect 函数放入一个小型 script,并让该 script 持续运行:
python
langwatch.agent.serve()   # Python, at the bottom of the script
typescript
// TypeScript: keep the script alive yourself. The WebSocket holds the event loop
// while connected, but the HTTP fallback and the reconnect wait do not.
setInterval(() => {}, 1 << 30);
// Run it with the project's TypeScript runner, for example `tsx agent.ts`
然后在另一个 terminal 中通过 CLI 确认:
bash
langwatch agent list
Agent 对应的记录会显示
Online
,并包含 environment 和 instance count。
Offline
表示进程没有连接:请检查进程环境中是否设置了
LANGWATCH_API_KEY
、进程是否正在运行,以及是否允许向 LangWatch endpoint 发起出站 WSS 连接。当网络拒绝 WebSocket upgrade 时,SDK 会自动回退到 HTTP long polling;也可以通过
LANGWATCH_AGENT_TRANSPORT=http
从一开始就强制使用该传输方式。
然后读回平台登记的信息:
bash
langwatch agent get <agent-id> --format json
结果中包含
environment
、
status
、
instances
(每个进程的 hostname、pid、SDK 和版本)以及
parameters
:每个 run parameter 对应一条记录,其中包含
name
、
type
、
options
(由
Literal
、
Enum
或
z.enum
生成的封闭列表)、
defaultValue
和
description
。将其与函数签名进行比较。如果某个参数缺失,说明它被当成了 turn field;如果参数没有默认值,它仍会登记,但会成为每次 run 都必须提供的必填参数。如果选项列表缺失,说明 annotation 不是
Literal
、
Enum
或
z.enum
。修复代码并重启进程后,记录会在下次登记时更新。

Step 5: Run the first test suite

Step 5:运行第一个测试套件

Create a test suite, create one scenario about behavior this agent really has, file it into the test suite, and run it:
bash
langwatch test-suite create 'Smoke'

langwatch scenario create 'Order status question' \
  --situation "A customer asks about the status of a recent order" \
  --criteria "The agent looks up the order before answering,The agent gives a concrete delivery estimate" \
  --test-suite 'Smoke'

langwatch test-suite run 'Smoke' --target connected:support-agent --wait
  • --target connected:<name>
    runs the agent in
    development
    , the environment a process registers under by default. When no process is connected there but one other environment is online, the run uses that one.
    connected:<name>@<environment>
    names the environment, and
    connected:<agent-id>
    works the same way;
    langwatch agent list --format json
    prints the id. A name with a space goes in double quotes,
    --target "connected:ACME checkout"
    , and so does
    --wait-online "ACME checkout"
    : passed bare, the shell splits it and the command refuses the stray word.
  • Write the situation and the criteria from the agent's real behavior in this codebase, not from the example. Include at least one criterion about a tool call or a lookup, which the judge verifies against the agent's own traces.
  • --criteria
    takes one comma-separated string, so a criterion cannot contain a comma. Rephrase instead.
  • --test-suite
    files the scenario into a test suite that exists. Create the test suite first.
  • Repeat
    --target
    to compare two environments, or the same agent with two parameter values:
    --target 'connected:support-agent@production?model=gpt-5'
    .
  • --wait
    blocks until the run finishes and exits non-zero when it fails. Use it here: the report in Step 6 needs the result.
  • Keep the agent process running for the whole run. A run against an agent with no connected instance is refused with
    agent_offline
    .
When the first run is green and the agent declares a parameter with an option list, offer one comparison run across the options. It reuses the same test suite, and the results page shows one column per value:
bash
langwatch run-plan run --test-suite 'Smoke' \
  --target 'connected:support-agent?model=gpt-5' \
  --target 'connected:support-agent?model=gpt-5-mini' \
  --name 'Smoke: model comparison' --format json
--param plan=pro
on the same command sets a value every target shares. The platform refuses a value outside the option list before anything is scheduled, and a name that neither the scenario nor the target agent declares is refused the same way, so read the names from
langwatch scenario get
and
langwatch agent get
rather than from memory.
创建测试套件,创建一个描述该 Agent 真实行为的 scenario,将 scenario 归档到测试套件中,然后运行:
bash
langwatch test-suite create 'Smoke'

langwatch scenario create 'Order status question' \
  --situation "A customer asks about the status of a recent order" \
  --criteria "The agent looks up the order before answering,The agent gives a concrete delivery estimate" \
  --test-suite 'Smoke'

langwatch test-suite run 'Smoke' --target connected:support-agent --wait
  • --target connected:<name>
    会在
    development
    中运行 Agent,这也是进程默认登记的 environment。当该 environment 中没有进程连接,但其他某个 environment 有在线进程时,run 会使用那个环境。
    connected:<name>@<environment>
    用于指定 environment,
    connected:<agent-id>
    的作用相同;
    langwatch agent list --format json
    会输出 id。包含空格的名称必须放在双引号中,例如
    --target "connected:ACME checkout"
    ,
    --wait-online "ACME checkout"
    也同样如此;如果直接传入,shell 会拆分字符串,命令会拒绝多出的单词。
  • 应根据此代码库中 Agent 的真实行为编写 situation 和 criteria,不要照搬示例。至少应包含一条与 tool call 或 lookup 有关的 criterion,judge 会使用 Agent 自己的 traces 验证它。
  • --criteria
    接受一个以逗号分隔的字符串,因此单条 criterion 不能包含逗号。请改写措辞。
  • --test-suite
    会将 scenario 归档到一个已经存在的测试套件中。请先创建测试套件。
  • 重复传入
    --target
    可以比较两个 environment,或使用两组参数值运行同一个 Agent:
    --target 'connected:support-agent@production?model=gpt-5'
    。
  • --wait
    会阻塞直到 run 完成,失败时以非零状态退出。此处应使用它,因为 Step 6 的报告需要运行结果。
  • 整个 run 期间都必须保持 Agent 进程运行。没有任何已连接实例时,run 会以
    agent_offline
    被拒绝。
当第一次运行通过,并且 Agent 声明了带选项列表的参数时,应提供一个跨选项的比较 run。它会复用同一个测试套件,结果页面会为每个值显示一列:
bash
langwatch run-plan run --test-suite 'Smoke' \
  --target 'connected:support-agent?model=gpt-5' \
  --target 'connected:support-agent?model=gpt-5-mini' \
  --name 'Smoke: model comparison' --format json
在同一命令中使用
--param plan=pro
,可以设置所有 target 共用的值。平台会在调度任何任务之前拒绝选项列表之外的值;scenario 或 target Agent 都没有声明的名称也会以同样方式被拒绝。因此,应从
langwatch scenario get
和
langwatch agent get
读取名称,不要凭记忆猜测。

Step 6: Report the result

Step 6:报告结果

Report to the user:
  • The Agent Testing page URL of their LangWatch project (
    https://app.langwatch.ai/<project-slug>/agent-testing
    , or the same path on their own instance when self-hosted).
  • What changed in the codebase: the decorated function, the parameters it declares, the environment it registers under.
  • The result of the first run.
  • The parameters the platform registered, as
    langwatch agent get
    lists them, so the user knows which levers the run dialog offers. The
    scenarios
    skill reads the same list when it proposes scenarios; its prompt is "Add scenario tests for my agent".
Report failures as they happened. If a CLI command failed or the platform was unreachable, name the step that failed and what the failure means for the user, and stop there. Do not paste the raw error text, stack trace or debug URL: those can contain secrets and tell the user nothing they can act on. Do NOT claim a scenario or a test suite ran when it did not.
向用户报告:
  • LangWatch 项目的 Agent Testing 页面 URL(
    https://app.langwatch.ai/<project-slug>/agent-testing
    ;如果是自托管实例,则使用相同路径)。
  • 代码库中发生了哪些变更:带 decorator 的函数、它声明的参数,以及它登记时使用的 environment。
  • 第一次运行的结果。
  • 平台登记的参数,按照
    langwatch agent get
    列出的内容报告,让用户知道 run dialog 中提供了哪些可调选项。
    scenarios
    skill 在提出 scenario 时也会读取同一份列表;它的 prompt 是“Add scenario tests for my agent”。
应如实报告发生的失败。如果某个 CLI 命令失败或平台无法访问,请说明失败发生在哪个步骤,以及该失败对用户意味着什么,然后停止。不要粘贴原始错误文本、stack trace 或 debug URL:这些内容可能包含 secrets,而且用户无法据此采取行动。不要在 scenario 或测试套件实际没有运行时声称它们已经执行。

When the change goes into a pull request

当变更进入 pull request 时

A change to the connect call is not live until the process that holds it starts again, so the registration you report has to come from a process you restarted in this conversation. Before you run
gh pr create
:
  1. Restart the service that holds the connect call, on the same port.
  2. Run
    langwatch agent get "<name>"
    and read the parameter list back.
  3. Put that list in the pull request body, as the command printed it.
When you could not restart the process, write in the body, in one line, that the restart is left to the user and that the parameters are not registered yet. A sentence such as "confirmed the connected agent registered both options" is false unless the
agent get
output in this conversation lists both options. Copy the pull request address that
gh pr create
prints into your reply as well, character for character.
A connected setup shows, on the run page: the conversation transcript, a trace link on each turn opening the agent's own spans, and judge reasoning that cites spans.
connect 调用发生变更后,在持有该调用的进程重新启动之前,它不会生效。因此,你报告的登记结果必须来自本次对话中已经重启的进程。运行
gh pr create
前:
  1. 在相同端口上重启持有 connect 调用的服务。
  2. 运行
    langwatch agent get "<name>"
    并读回参数列表。
  3. 将该列表按命令输出原样放入 pull request 正文。
如果无法重启进程,应在正文中用一行说明重启工作需留给用户完成,并且参数尚未登记。除非本次对话中的
agent get
输出明确列出了两个选项,否则“已确认连接的 Agent 登记了两个选项”之类的说法都是虚假报告。还应逐字符复制
gh pr create
输出的 pull request 地址,并放入回复中。
已连接的设置会在 run 页面中显示 conversation transcript、每一轮对应的 trace link(打开后可见 Agent 自己的 spans),以及引用 spans 的 judge reasoning。

Plan Limits

Plan Limits

LangWatch's free plan has limits on prompts, scenarios, evaluators, experiments, and datasets. When you hit a limit, the API returns
"Free plan limit of N reached..."
with an upgrade link.
How to handle:
  • Work within the limits. If 3 resources of the relevant type are allowed, create 3 meaningful ones, not 10.
  • Make every creation count: each one should demonstrate clear value.
  • Show what works FIRST. If you hit a limit, summarize what was accomplished and note that upgrading the plan raises it. Point to the subscription settings on the platform, or to the license settings if the CLI is pointed at a self-hosted endpoint. Read the endpoint the CLI actually uses, which can come from
    .env
    , from the process environment, or from the saved CLI configuration.
  • Do NOT delete existing resources to make room or repurpose an existing resource to evade the limit.
LangWatch 免费套餐对 prompts、scenarios、evaluators、experiments 和 datasets 有限制。达到限制时,API 会返回
"Free plan limit of N reached..."
以及升级链接。
处理方式:
  • 在限制内工作。如果相关类型允许创建 3 个资源,就创建 3 个有实际意义的资源,而不是 10 个。
  • 确保每次创建都有价值:每个资源都应展示清晰的作用。
  • 优先展示已经完成的内容。如果达到限制,应总结已完成的工作,并说明升级套餐可以提高限制。指向平台中的 subscription settings;如果 CLI 指向自托管 endpoint,则指向 license settings。应读取 CLI 实际使用的 endpoint,它可以来自
    .env
    、进程环境或保存的 CLI 配置。
  • 不要删除已有资源来腾出空间,也不要重新利用已有资源来规避限制。

Fallback: register an HTTP agent

Fallback:登记 HTTP Agent

Use this path ONLY when the decorator is impossible: the agent is written in a language with no LangWatch SDK, or the code is out of reach and only a URL is available. The platform then calls a public URL once per conversation turn, and the setup needs three extra pieces of work: a reachable endpoint, a body template, and a
traceparent
middleware.
仅当 decorator 无法实现时,才使用此路径:即 Agent 所用语言没有 LangWatch SDK,或者无法访问代码,只能获得一个 URL。此时,平台会在每个 conversation turn 调用一次公开 URL,配置还需要额外完成三项工作:提供可达 endpoint、body template 和
traceparent
middleware。

Locate the endpoint

定位 Endpoint

Find the HTTP endpoint that takes a user message and returns the agent's reply. If none exists, add one: accept a JSON body carrying the conversation messages, run the agent, and return the reply text in a JSON field, for example
{"reply": "..."}
.
找到接收用户消息并返回 Agent reply 的 HTTP endpoint。如果不存在,则添加一个:接收包含 conversation messages 的 JSON body,运行 Agent,并在 JSON 字段中返回 reply 文本,例如
{"reply": "..."}
。

Wire authentication for scenario traffic

为 Scenario Traffic 配置认证

Understand the endpoint's authentication before touching it.
  • If the endpoint accepts a fixed token in a header, use that credential as-is in the registration. Change nothing on the server.
  • If the normal authentication is built for human users (sessions, cookies, OAuth redirects), add a dedicated authentication path for scenario traffic: the server reads the expected key from an environment variable such as
    SCENARIO_API_KEY
    and checks it against the
    Authorization: Bearer
    header on each request. A request carrying the valid key is accepted; every other request goes through the existing authentication unchanged. If
    SCENARIO_API_KEY
    is unset, the path is off.
NEVER weaken, bypass, or remove the existing authentication for normal traffic. The scenario path is additive, and the dedicated key is what the user revokes to close it.
Store the key as a project secret so the registration references it instead of holding it:
bash
langwatch secret create SCENARIO_API_KEY --value "<key>"
修改 endpoint 之前,先了解它使用的认证方式。
  • 如果 endpoint 接受 header 中的固定 token,请在登记时原样使用该凭据,不要修改服务器。
  • 如果正常认证面向人类用户,例如 session、cookie 或 OAuth redirect,请为 scenario traffic 添加专用认证路径:服务器从
    SCENARIO_API_KEY
    等环境变量读取预期 key,并在每次请求时将其与
    Authorization: Bearer
    header 进行比较。携带有效 key 的请求会被接受;其他所有请求继续使用原有认证流程。如果未设置
    SCENARIO_API_KEY
    ,该路径保持关闭。
绝不能削弱、绕过或删除正常 traffic 的现有认证。Scenario 路径是附加路径,只有专用 key 能控制它的启用和关闭。
将 key 保存为 project secret,使登记配置引用该 secret,而不是直接持有它:
bash
langwatch secret create SCENARIO_API_KEY --value "<key>"

Adopt the trace context

接纳 Trace Context

This step belongs to the HTTP path only. The platform sends a W3C
traceparent
header on every call, one trace per conversation turn. When the server adopts it, the spans the agent produces land in that same trace, and the judge reads them before its verdict. Without it the judge can only grade the reply text, and criteria about tool calls or lookups come back inconclusive.
  • If the service uses OpenTelemetry HTTP auto-instrumentation, adoption already happens. Verify it in the code and change nothing.
  • Otherwise, attach the extracted context in a middleware that runs before any tracing starts. Do not extract inside the handler body: a handler decorated with
    @langwatch.trace()
    opens its root span before the body runs, so an extraction there is too late and the agent's spans land in a separate trace. The middleware placement covers every tracing style: decorators,
    with langwatch.trace()
    , autotrack, community instrumentations, and plain OpenTelemetry spans.
Python (ASGI middleware, e.g. FastAPI):
python
from opentelemetry import propagate
from opentelemetry.context import attach, detach

@app.middleware("http")
async def adopt_remote_trace(request, call_next):
    token = attach(propagate.extract(dict(request.headers)))
    try:
        return await call_next(request)
    finally:
        detach(token)
For Flask, attach in
before_request
(keep the token on
g
) and detach in
teardown_request
.
TypeScript (middleware, registered before the routes):
typescript
import { context, propagation } from "@opentelemetry/api";

app.use((req, res, next) => {
  const ctx = propagation.extract(context.active(), req.headers);
  context.with(ctx, () => next());
});
The TypeScript middleware needs an initialized OpenTelemetry runtime: a registered context manager and propagator. The LangWatch SDK's
setupObservability()
and the OpenTelemetry
NodeSDK
both register them at startup; without one of them,
context.with
and
propagation.extract
are no-ops.
Confirm the agent reports its traces to the same LangWatch project that runs the scenarios (the same
LANGWATCH_API_KEY
project). Traces sent to another project, or to another observability backend only, are invisible to the judge. If the service has no LangWatch tracing yet, set it up with the
tracing
skill; its prompt is "Instrument my code with LangWatch".
此步骤仅适用于 HTTP 路径。平台会在每次调用时发送 W3C
traceparent
header,每个 conversation turn 对应一个 trace。当服务器接纳该 context 后,Agent 产生的 spans 就会落在同一个 trace 中,judge 会在作出结论前读取它们。如果没有这一过程,judge 只能评估 reply 文本,涉及 tool call 或 lookup 的 criteria 将无法得出明确结论。
  • 如果服务使用 OpenTelemetry HTTP auto-instrumentation,则已经完成 context 接纳。请在代码中验证,不要做任何修改。
  • 否则,应在任何 tracing 开始之前运行的 middleware 中附加提取出的 context。不要在 handler body 内提取:由
    @langwatch.trace()
    装饰的 handler 会在 body 运行前打开 root span,因此此时提取已经太晚,Agent 的 spans 会落在另一个 trace 中。Middleware 的位置能够覆盖所有 tracing 风格:decorators、
    with langwatch.trace()
    、autotrack、community instrumentations,以及普通的 OpenTelemetry spans。
Python(ASGI middleware,例如 FastAPI):
python
from opentelemetry import propagate
from opentelemetry.context import attach, detach

@app.middleware("http")
async def adopt_remote_trace(request, call_next):
    token = attach(propagate.extract(dict(request.headers)))
    try: return await call_next(request)
    finally: detach(token)
对于 Flask,应在
before_request
中附加 context(将 token 保存在
g
上),并在
teardown_request
中分离。
TypeScript(middleware,应在 routes 之前注册):
typescript
import { context, propagation } from "@opentelemetry/api";

app.use((req, res, next) => {
  const ctx = propagation.extract(context.active(), req.headers);
  context.with(ctx, () => next());
});
TypeScript middleware 需要已初始化的 OpenTelemetry runtime,包括已注册的 context manager 和 propagator。LangWatch SDK 的
setupObservability()
和 OpenTelemetry
NodeSDK
都会在启动时注册它们;如果两者都不存在,
context.with
和
propagation.extract
将不会执行任何操作。
确认 Agent 将 traces 报告到运行 scenario 的同一个 LangWatch 项目,也就是使用同一个
LANGWATCH_API_KEY
的项目。报告到其他项目或仅报告到其他 observability backend 的 traces 对 judge 不可见。如果服务尚未接入 LangWatch tracing,请使用
tracing
skill 进行设置;它的 prompt 是“Instrument my code with LangWatch”。

ASK where the agent runs

询问 Agent 的运行位置

Ask the user for the URL where this service is deployed, and wait for the answer. A staging deployment is the recommended target: it exercises the real system without touching production data. Any URL the LangWatch backend can reach works; an internal hostname or a firewalled service does not.
If the agent only runs on the user's machine, register it as below and then run
langwatch agent tunnel --port <port> --agent <agent-id>
, which opens a tunnel to the local port and points the registered agent at it for the session. Keep it running while test suites execute; Ctrl-C restores the previous URL.
询问用户该服务的部署 URL,并等待用户回答。推荐使用 staging deployment:它可以测试真实系统,同时不会影响 production data。任何 LangWatch backend 能够访问的 URL 都可以;internal hostname 或受防火墙保护的服务无法访问。
如果 Agent 只在用户机器上运行,先按以下方式登记,然后运行
langwatch agent tunnel --port <port> --agent <agent-id>
。该命令会为本地 port 建立 tunnel,并在 session 期间让已登记的 Agent 指向该地址。执行测试套件时必须保持其运行;按 Ctrl-C 会恢复之前的 URL。

Register and run

登记并运行

Adjust
bodyTemplate
to the request shape the endpoint expects and
outputPath
to the JSONPath of the reply text in the endpoint's real response:
bash
langwatch agent create 'My Agent' --type http --config '{
  "url": "https://staging.example.com/chat",
  "bodyTemplate": "{\"thread_id\": \"{{ threadId }}\", \"messages\": {{ messages }}}",
  "outputPath": "$.reply",
  "auth": {"type": "bearer", "token": "{{ secrets.SCENARIO_API_KEY }}"}
}'
If the endpoint creates its own conversation and returns its id, add
"sessionPath": "$.conversation_id"
(the JSONPath of that value in the response) and read it back as
{{ session }}
in the body template, the URL or a header on the next turn of the same conversation. It is empty on the first turn.
The body template renders as a Liquid template on every turn. The URL and header values render the same variables:
VariableValue
{{ messages }}
The whole conversation as a raw JSON array of
{role, content}
messages
{{ input }}
The text of the last user message
{{ threadId }}
A conversation id, the same on every turn of a run
{{ params.NAME }}
A run parameter the scenario declares
{{ traceId }}
,
{{ traceparent }}
The turn's trace identifiers, for systems that read them from the body or a custom header instead of the
traceparent
header
Run it with
--target http:<agent-id>
, and follow Step 5 and Step 6 otherwise unchanged.
根据 endpoint 实际期望的请求结构调整
bodyTemplate
,并根据真实响应中 reply 文本的 JSONPath 调整
outputPath
:
bash
langwatch agent create 'My Agent' --type http --config '{
  "url": "https://staging.example.com/chat",
  "bodyTemplate": "{\"thread_id\": \"{{ threadId }}\", \"messages\": {{ messages }}}",
  "outputPath": "$.reply",
  "auth": {"type": "bearer", "token": "{{ secrets.SCENARIO_API_KEY }}"}
}'
如果 endpoint 会创建自己的 conversation 并返回其 id,请添加
"sessionPath": "$.conversation_id"
,即响应中该值的 JSONPath,并在同一 conversation 的下一轮中,通过 body template、URL 或 header 将其作为
{{ session }}
读回。第一轮中它为空。
Body template 每一轮都会作为 Liquid template 渲染。URL 和 header 的值支持渲染相同的变量:
VariableValue
{{ messages }}
完整 conversation,以原始 JSON 数组表示,其中包含
{role, content}
messages
{{ input }}
最后一条 user message 的文本
{{ threadId }}
Conversation id,在一次 run 的每一轮中保持相同
{{ params.NAME }}
Scenario 声明的 run parameter
{{ traceId }}
,
{{ traceparent }}
Turn 的 trace identifiers,适用于从 body 或自定义 header 读取它们的系统,而不是从
traceparent
header 读取
使用
--target http:<agent-id>
运行,其余步骤与 Step 5 和 Step 6 相同。

Common Failures

常见故障

SymptomCauseFix
langwatch agent list
shows no row for the agent
The process started without an API key, so the SDK opened no connection.Set
LANGWATCH_API_KEY
in the process environment and restart the process.
The row reads
Offline
The process stopped, or its outbound connection is blocked.Restart the process. On a network that blocks WebSockets, set
LANGWATCH_AGENT_TRANSPORT=http
; the SDK then registers over HTTP long polling.
The process logs
timed out during opening handshake
on every attempt while
curl
reaches the endpoint
The host resolves the endpoint to an IPv6 address it cannot reach, most often behind a VPN tunnel that drops IPv6, and an older SDK waited on that address alone.Update the
langwatch
package to the latest release: it moves on to the next address after 0.25 s (
LANGWATCH_AGENT_HAPPY_EYEBALLS_DELAY
tunes it). Disconnecting the VPN also clears it.
langwatch agent get
lists a parameter without
options
, or does not list it at all
The annotation is not a
Literal
, an
Enum
or a
z.enum
, or the parameter has no default and the platform reads it as required.
Change the annotation or add the default, then restart the process.
The run is refused with
agent_offline
No instance was connected when the run started.Start the agent process and run again.
The run is refused with
agent_owner_only
The agent registered under
development
with a personal key, so only its owner can run it.
Run it as the owner, or register it under a shared environment name such as
dev-shared
.
A new agent row appears on every start, each with a different host labelThe agent registered under
development
with a project key from a container, and the container's hostname changes on every recreate.
Set
LANGWATCH_AGENT_ENVIRONMENT
to a name other than
development
, or pin the container's hostname with
hostname:
in Compose or
--hostname
on
docker run
.
The run is refused with
scenario_parameter_option_invalid
A value is outside the closed option list the agent declares.Use one of the listed options, or widen the
Literal
(Python) or
z.enum
(TypeScript) list in the code.
A turn fails with
agent_call_timeout
The call took longer than the agent's timeout.Raise
timeout
on the connect function (up to 300 seconds), or make the agent answer faster.
Trace-dependent criteria come back inconclusiveThe agent reports its traces to a different LangWatch project, or it reports none at all.Point the agent's tracing at the same project's API key. Set it up with the
tracing
skill.
SymptomCauseFix
langwatch agent list
中没有 Agent 对应记录
进程启动时没有 API key,因此 SDK 没有建立连接。在进程环境中设置
LANGWATCH_API_KEY
,然后重启进程。
记录显示
Offline
进程已停止,或者出站连接被阻止。重启进程。在阻止 WebSocket 的网络上,设置
LANGWATCH_AGENT_TRANSPORT=http
;SDK 随后会通过 HTTP long polling 登记。
每次尝试都记录
timed out during opening handshake
,但
curl
可以访问 endpoint
Host 将 endpoint 解析到了无法访问的 IPv6 地址,这常见于会丢弃 IPv6 的 VPN tunnel;旧版 SDK 只会等待该地址。将
langwatch
package 更新到最新版本;新版本会在 0.25 秒后尝试下一个地址,可通过
LANGWATCH_AGENT_HAPPY_EYEBALLS_DELAY
调整。断开 VPN 也能解决问题。
langwatch agent get
列出了某个参数,但没有
options
,或者根本没有列出该参数
Annotation 不是
Literal
、
Enum
或
z.enum
,或者参数没有默认值,因此平台将其视为必填项。
修改 annotation 或添加默认值,然后重启进程。
Run 因
agent_offline
被拒绝
Run 启动时没有已连接的实例。启动 Agent 进程,然后重新运行。
Run 因
agent_owner_only
被拒绝
Agent 使用个人 key 登记在
development
下,因此只有其所有者可以运行。
以所有者身份运行,或将其登记在
dev-shared
等共享 environment 名称下。
每次启动都会出现新的 Agent 记录,且每条记录都有不同的 host 标签Agent 使用来自 container 的项目 key 登记在
development
下,而 container 每次重新创建时 hostname 都会变化。
将
LANGWATCH_AGENT_ENVIRONMENT
设置为
development
之外的名称,或在 Compose 中使用
hostname:
、在
docker run
中使用
--hostname
固定 container hostname。
Run 因
scenario_parameter_option_invalid
被拒绝
值不在 Agent 声明的封闭选项列表中。使用列出的选项之一,或在代码中扩大 Python
Literal
或 TypeScript
z.enum
列表。
Turn 因
agent_call_timeout
失败
调用耗时超过了 Agent 的 timeout。提高 connect 函数上的
timeout
(最高 300 秒),或让 Agent 更快返回。
依赖 trace 的 criteria 无法得出明确结论Agent 将 traces 报告到了不同的 LangWatch 项目,或者根本没有报告 traces。将 Agent 的 tracing 指向同一个项目的 API key。使用
tracing
skill 进行设置。

Common Mistakes

常见错误

  • Do NOT reimplement the agent inside the connect function, and do NOT point it at a simplified copy. It calls the agent the product already runs, so the simulation exercises the real code path.
  • Do NOT decorate a function the app already has when it returns its own result object instead of reply text. Write the connect function beside it, call it, and return the text.
  • Do NOT add a runner script or a second start command when the service already has one. The connect function goes on the existing startup path.
  • Do NOT add a
    traceparent
    middleware for a connected agent. The SDK adopts the trace context itself; the middleware belongs to the HTTP fallback only.
  • Do NOT hardcode an environment string, and do NOT write a fallback such as
    process.env.APP_ENV ?? "development"
    . It overrides
    LANGWATCH_AGENT_ENVIRONMENT
    and registers a production process under
    development
    . Let the SDK resolve the environment.
  • Do NOT declare a run parameter for a value the tests never vary. Every declared parameter appears in the run dialog.
  • Do NOT use a turn field name (
    messages
    ,
    new_messages
    ,
    thread_id
    ,
    session
    ,
    trace_id
    ) as a run parameter name.
  • Do NOT invent an agent id or pass a URL as the run target. Read the identity from
    langwatch agent list --format json
    .
  • Do NOT pass
    --test-suite
    a test suite the project does not hold. The command refuses it; create the test suite first with
    langwatch test-suite create
    .
  • Do NOT stop the agent process while a run is executing. Every turn of the run calls it.
  • Do NOT reach for the HTTP fallback because the decorator looks like more work. It is fewer steps: no public URL, no body template, no credential in the agent configuration, no middleware.
  • Do NOT report success when a command failed. An unreachable platform or a failed run is part of the report, named per step.
  • Do NOT write a registration into a pull request body without the
    langwatch agent get
    output that shows it. A body that states what no command in the conversation printed is a false report.
  • 不要在 connect 函数中重新实现 Agent,也不要让它指向简化副本。connect 函数应调用产品已经运行的 Agent,使 simulation 实际执行真实代码路径。
  • 如果应用已有的函数返回自己的结果对象而不是 reply 文本,不要直接为它添加 decorator。应另写 connect 函数、调用已有函数,并返回文本。
  • 如果服务已经有启动方式,不要添加 runner script 或第二个启动命令。connect 函数应放在现有启动路径上。
  • 不要为 connected Agent 添加
    traceparent
    middleware。SDK 会自行接纳 trace context;该 middleware 仅用于 HTTP fallback。
  • 不要硬编码 environment 字符串,也不要编写
    process.env.APP_ENV ?? "development"
    之类的 fallback。它会覆盖
    LANGWATCH_AGENT_ENVIRONMENT
    ,并把 production 进程登记为
    development
    。应让 SDK 解析 environment。
  • 不要为测试永远不会改变的值声明 run parameter。每个已声明参数都会出现在 run dialog 中。
  • 不要将 turn field 名称(
    messages
    、
    new_messages
    、
    thread_id
    、
    session
    、
    trace_id
    )用作 run parameter 名称。
  • 不要虚构 Agent id,也不要将 URL 作为 run target。应从
    langwatch agent list --format json
    读取身份信息。
  • 不要向
    --test-suite
    传入项目中不存在的测试套件。命令会拒绝该操作;应先使用
    langwatch test-suite create
    创建测试套件。
  • Run 执行期间不要停止 Agent 进程。Run 中的每一轮都会调用它。
  • 不要仅仅因为 decorator 看起来工作量更大就使用 HTTP fallback。HTTP fallback 的步骤实际上更少:不需要公开 URL、body template、Agent 配置中的凭据或 middleware。
  • 命令失败时,不要报告成功。平台无法访问或 run 失败都属于报告内容,应明确指出失败发生在哪个步骤。
  • 如果没有本次对话中
    langwatch agent get
    输出的证明,不要在 pull request 正文中写入登记结果。正文中陈述任何命令未曾输出的内容,都属于虚假报告。