Windows Live Ag...'s profileWindows Live AgentsPhotosBlogLists Tools Help

Blog


    August 12

    New Windows Live Agents Blog Location

    Greetings,

    Windows Live Agents Blog has been moved.

    New location is: http://blogs.msdn.com/windowsliveagents/

    Thank you,

    Windows Live Agents Team.

    August 04

    PHI Hosting Process

    PHI - Hosting Process

    Partner Hosting Infrastructure, or PHI, is a tool that will allow developers to apply for Microsoft to host their Agent projects. Developers will be able to upload their code, manage projects, and be informed of the current state of their projects. This section will go over the tools that developers have available to them when developing a project, taking it live, and making changes to project files.

    The following is a quick overview of the PHI process, followed by a detailed explanation of the hosting process.

    • Initial project application – log in to PHI and click on “Apply for New Agent”. Fill in the application form.
    • Apply for Hosting – after your application is filled in, you will be in the “Initial” state. Once you are satisfied with your application, click “Apply for Hosting”. Microsoft will then review your project application. Don’t forget to sign and submit the Hosting Agreement.
    • Project check-in – once your project application has been approved and Microsoft has received your signed Hosting Agreement, you can check in your project. For details on how to do this, see Step 2 below.
    • Hosting – Once the project is checked in, Microsoft will review your project files and get them hosted in the data center. You must provide a screen name for your Agent before being hosted. You will then be able to talk to your Agent online. You can edit your Agent at any time.
    • Going Live – Once you are satisfied with your Agent’s content, click “Submit Project Details for Review”. This will trigger another review by Microsoft, after which your project will go live.

     

    A more detailed description:

    Step 1: Initial Project Application

     

    1.     Let’s assume that you have already created a project called sampleProj and you now want it to be hosted and take it live.

    2.     Go to http://phi.agents.live.com

    3.     In the top right corner, click on the “Sign In” link and sign in with your WLID.

    4.     In the “Agents Home” page, click on “Apply for New Agent”.

    5.     Enter “sampleProj” as the Agent Name, and fill out the rest of the form.

    6.     Ensure that you complete, sign, and send back the Hosting Agreement (if this is your first Agent) or the Statement of Work (if you have already completed a Hosting Agreement).

    7.     Once you have filled out and sent back the appropriate documents, check the “I have signed and sent back the hosting agreement.” checkbox and then click “Save”. You will be taken back to the Agents Home page, and your project should be in the “Initial” state.

     

    Step 2: Check in Code for a Project

     

    To be able to check in your project code, you must first apply for hosting. To do this:

    1.     Click the “Apply for Hosting” link next to your new project.

    2.     Click the “Apply for Hosting” button

    3.     You should be taken back to the landing page, and the project state should now be “Awaiting Initial Project Hosting Approval by Microsoft”.

    At this point, you must wait for the reviewers at Microsoft to go over your application and approve it. You will know that a project has been approved when the state changes from “Awaiting Initial Project Hosting Approval by Microsoft” to “Waiting for Legal Documents from Developers”. The reviewers will need to ensure that they have received legal documents from you. If they have, they will update your project to the “Awaiting Initial Project Checkin by Developer” state.

    Once your project is in this state, you can check in your code. To do so:

    1.     Open up your project in the Visual Studio IDE.

    2.     Go to Tools -> Windows Live Agents Tools ->Code Management -> Sign in to Windows Live

    3.     Sign in using the same WLID that you applied for the project with in PHI

    4.     Go to Tools -> Windows Live Agents Tools -> Code Management -> Check In Project

    5.     Select the project. Make sure it has the SAME NAME as the project you applied for in PHI (in our case, sampleProj). Then click “Check In”.

    6.     Note: This initial check in will be version 2. This is expected.

    Your project is now checked in!

    Step 3: Hosting and Testing Your Project

     

    After checking in your code, the state of your project should be “Awaiting Critical File Review by Microsoft”. Microsoft will need to review your critical files (in this stage, the only critical file is the .connections file) before proceeding.

    Once your code review is in progress, your project will be in the “Initial Critical Files Under Review by Microsoft” state.

    If the files are approved, the state will change to “Pending Hosting in Data Center by Microsoft”. This means that you are waiting for the Operations team to move your files to the data center. Once this is complete, your project will be hosted, and the state will change to “Hosted”. At this point, your files are live in our data center.

    From the Hosted state and going forward, files can be modified and checked in at any time. The only file modifications that will require review are checkins of the connections file. You can think of being in the Hosted state as being in a staging environment.

    To talk to your agent on Messenger, you will need to add a staging screen name to your project. To do this, click on the “Manage Screen Names” link in the “Actions” column next to your project. Enter a WLID that you would like for your staging screen name. The WLID will be validated before being added to your project.

    Step 4: Going Live

     

    Once you are satisfied that your project is ready to go live, you need to submit your project for review. To do this:

    1.     Click the “Submit Project Details for Review” link in the “Actions” column next to your project.

    2.     Click the “Submit Project Details for Review” button.

    3.     The project state should now be “Hosted: Awaiting Go-Live Approval from Microsoft”.

    Once the Microsoft reviewers have reviewed and are satisfied with your project, they will submit the project files to Ops for deployment in the data center. The state of your project will change to “Hosted: Pending Go-Live in Data Center by Microsoft”.

    Once your project has been put in the data center, the project state will be updated to “Live”.

    To add your live screen name and talk to your Agent via Messenger, you again use the “Manage Screen Names” link on your PHI landing page. Enter the WLID that you would like your Agent to have. The WLID will be validated before it is added to the project.

     

    How to Modify Files

     

    You have two options available to you when you would like to modify files in your project.

    Using the Console

    You can use the KMS Console at https://sampleProj.console.agents.live.com to modify the project. This will allow you to edit topics and responses fairly easily. For more control, however, you would want to use the IDE to make project changes.

    Using the IDE

    You can use the IDE to check in files in the same way that you checked in the project originally. Please note that using the IDE will check in an entire project, and does not allow you to check in just a single file.

    To check out using the IDE, you must first sign in and then acquire the lock by clicking on Tools -> Windows Live Agents Tools -> Code Management -> Check Out Project. You can then edit whichever files you wish. While a project is checked out to you, no one will be able to edit any of the project files using the IDE or KMS.

    To check in, click on Tools -> Windows Live Agents Tools -> Code Management -> Check In Project.

     

    Checking In the Connections File

    When you check in the connections file, it must be reviewed and approved before being hosted or going live in the data center. Check in the connections file as you would check in any other file (see directions above).

    Checking in your project when the .connections file has not been modified will not trigger a review. Those changes should propagate immediately.

     

    Adding Project Developers/Contributors

     

    Besides the project owner, there are two other roles that you may have:

    ·         Developer – A developer is allowed to check out, modify, and check in a project

    ·         Consumer – A consumer is allowed to check out a project, but all project files will be read-only. They cannot check in any changes.

    To add a developer or consumer to your project, click on the “Manage Developers” link in the “Actions” column next to your project name. Enter a valid Windows Live ID that your developer/consumer will be using to log in, and select the appropriate role.

     

    Summary

     

    Using PHI will allow you to apply for hosting and track your project online. It also allows you to upload your code using the Visual Studio SDK. The tools that are available in PHI make it much easier for developers to get projects hosted in the Microsoft data center.

     

    July 29

    Visual Studio hints

    If you are moving from the old Colloquis IDE to the Visual Studio-based Windows Live Agents SDK, and you've never used Visual Studio, you may be a little lost.  This post aims to collect simple hints and tricks for agent developers new to working with Visual Studio.  This is just a start -- if you have any hints, please do comment, and later on we'll post a sequel. 
     
    Also, this is by no means much of a guide to Visual Studio as a whole, and is really meant to get you started and oriented for agent-development tasks.  Also, it's not a guide for creating agents.  The first resource for using the new SDK is the Windows Live Agents SDK documentation.  You should start there before doing anything.
     
    Agents menus
    Here are some common menu locations for agent-development tasks:
     
    Edit -> Find and Replace   ... Lots of options for searching through single or multiple files.  Regular expressions work!
    View -> Solution Explorer ... This is rougly equivalent to the Explorer feature in the old IDE, and lets you see the project files in a tree view.
    View -> Class View          ... Lets you see all the project's domains and packages as a flat list of classes.
    View -> Error list             ... Displays compile errors and warnings.
    View -> Output                ... Similar to the "Misc Debug" pane of the old IDE, displays compiler messages.
    View -> Other Windows -> Conversation Window            ... Where you compile and talk to the agent.
    View -> Other Windows -> Comprehension Info Window  ... Displays match scoring information.
    Project -> Add DLS Item                ... Add datasources and other DLS items.
    Project -> ProjectName Properties  ... Specify compile parameters like buddy id, filter, and command-line options.
    Tools -> Windows Live Agents Tools -> Code Management              ... The SDK's interface to the Partner Hosting Infrastructure.
    Tools -> Windows Live Agents Tools -> Request License Certificate  ... Get a new license cert.
    Tools -> Windows Live Agents Tools -> Management Console          ... Launches the web management console within Visual Studio.
    Tools -> Windows Live Agents Tools -> Update connections file        ... Attempts to update the connections file based on the content of your project.
    Tools -> Windows Live Agents Tools -> Performance                       ... Various tools for measuring performance.
    Tools -> Options ... Set things like tabs and syntax highlighting.
     
    Show All Files
    By default, the SDK will mount a filesystem in the Solution Explorer, which may affect performance if there are a lot of files, causing the editor to lag.  If this is the case, you can click "Show All Files" to remove the All Files view, and navigate to files through the Class View.  Editing will be a lot faster.  Set Show All Files to false by default at Tools -> Options -> Windows Live Agents SDK -> SDK Settings -> General -> "Show All Files on project open."
     
    Keyboard Shorcuts 
    As of right now, there isn't a keyboard shortcut for starting/stopping the agent in the Conversation Window.  However, here are some shorcuts you may find useful:
     
    CTRL-K CTRL-C ... comment
    CTRL-K CTRL-U ... uncomment
    CTRL-ALT-L ... Solution Explorer
    CTRL-TAB ... toggle through open windows
    CTRL-Shift-<arrow key> ... move forward/backward by one word
     
    Many other shortcuts are listed next to the menu items they correspond to.
     
    Window Arrangement
    One of the best things about Visual Studio is the flexibility it provides for laying out your environment.  Any window can be tabbed, docked, or floating, and you can drag everything around the way you like.  Right click a window title, or click the small arrow in the top of the pane, to see your options. 
     
    July 17

    Definition of Reporting Terms in Usage Reporting in 5.0

     

    When your agent launches, you will be given access to the Knowledge Management Server, which includes a Usage Reporting section. The URL is https://YourProjectName.console.agents.live.com.

     

    When you navigate to the Usage Reporting site, you will see date range options in the left pane, and report results in the right pane.  The default date is the present day.  You can switch days by clicking on the calendar on the left.

     

    If you click on “Custom Range” on the upper left side of the page, you can view usage reports for a custom date range. Please not that in this view, New & Unique Users will be shown as N/A. Due to a design limitation, we currently cannot provide accurate measurements of new and unique users for custom reporting periods.

     

    At the top of the page, you may see the following sections of reporting: Volume Summary, User Demographics, Languages Used, Activity Usage, Compliance, Category Analysis, Topic Analysis, and Clickthroughs. The default reporting view is Volume Summary. The sections of Usage Reporting you see, depends on how you have set up the usage_config.xml your project uses. For more information on customizing the usage_config.xml, click here.

     

    Definition of Reporting Terms: 

     

    Volume Summary Section

     

    Total Queries:             

    The total number of queries in all sessions within the specified time period. For example, if the time period includes 100 sessions, there could be 1000 or more total queries.

     

    Total Sessions:           

    The total number of sessions within the specified time period.

     

    Unique Users:             

    The number of unique users within the specified time period. Each user is counted once, regardless of how often they interact with the agent. For example, if one person has ten separate sessions with the agent during the specified time period, they are still identified as one unique user.

     

    New Users:                  

    The number of users whose initial session with the agent occurred during the specified time period.

     

    Average Queries Per Session:

    Total queries within the specified time period divided by total sessions within the specified time period.

     

    Average Sessions Per Unique Users:  

    Number of unique users within the specified time period divided by total number of sessions within the specified time period.

     

    Category & Topic Analysis Sections

     

    Category Analysis is broken out into two sections: Category Distribution Per Query and Category Distribution Per Session. Category Distribution Per Query breaks down how many queries for the chosen date range matched to a category. Category Distribution Per Session breaks down how many times a query matched on a Category during a session.

     

    Topic Analysis is only broken out into one section: Topic Distribution (By Query). This measures how many times users’ queries matched to a specific topic for the chosen date range.

     

    For information on how to log Category and Topic Analysis in your project, click here.

     

    Clickthrough Section

     

    The Clickthrough section breaks down how many times a user is presented a link and how many times a user clicks on the link.

     

    Total Impressions

    Number of times a user is presented a link in their conversation with the bot.

     

    Total Clicks

    Number of times a user clicks on the link he / she is presented.

     

    For information on tracking ClickThroughs in your project, click here.

     

    Languages Used Section

     

    Agent detects what languages users speak to the agent in.

     

    Activity Usage Section

     

    Tracks how many users accept or reject an invitation to open the Activity Window.

     

    Compliance Section

     

    Conversations Stopped

    Number of conversations interrupted after detecting user was typing sensitive topics beyond the scope of the agent.

     

    Sensitive Sequences Rejected

    Number of answers that were blocked by the output filter, displaying an error message to the user.

     

    Sensitive Sequences Trusted

    Number of answers that would have been blocked by the output filter, but were let through because of the use of the tag <trusted>.

     

    Answers Invoking Trust

    Number of messages that were displayed with the tag <trusted>…</trusted> in them. It includes both messages that would have passed the output filter or failed it.

     

    July 08

    An advanced look at Web Services and DataSources - Part II

    This is a continuation on An Advanced Look at Web Services and DataSources.  The original entry is located here:   http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!711.entry
     

    Now let’s take a look at the datasource itself.  The datasource essentially is a function itself, with arguments to pass, and variables to return. In the preprocess section, the POST_DATA variable where the XML SOAP request string was built is put into here.  In addition, the actual web service URL is stated here as well. 

     

    In the preprocess section, there are also two built-in variables that can be used, LIMIT and OFFSET.  These two variables are used to ‘page’ results in a cursor.  In the example above, we look at LIMIT to populate a variable called MAXRESULTS. The MAXRESULTS variable is then used in the COUNT element (in this case 10) to bring back 10 results per request.  If the user needs more, then the datasource then starts at the next row and retrieves 10 more results.

     

    The simple xml section is a hierarchical representation of the XML response from the SOAP API, to be flattened out into a 2-dimension look when the data is retrieved.  Indentation is used to signify a parent-child relationship. The {loop=content} statement acts as a loop within the XML, iterating through the XML.  The end nodes (highlighted in BOLD) are the fields that is used to capture information and passed back to the calling routine.  Note that fields can be skipped in the simple xml section if the user does not need it.

     

    It should be noted here that by using simple xml to represent the XML response, there is no provision for providing a “dynamic” representation of the XML using simple xml.  So in essence, you would have to potentially write a different datasource function for each different search type in this case.  For generate an advanced datasource that could output differently depending on the search type would require outputting a datasource in Buddyscript. We’ll cover this in a different blog.

     

     

    datasource LiveSearchAPI(SEARCH, CULTURE_INFO) => Title, Description, Url, Source, NewsYear, NewsMonth, NewsDay, NewsHour, NewsMinute, NewsSecond {expire="in 1 hour" continue_on_error="true" timeout="15" }

      preprocess

        if LIMIT>10 || LIMIT<=0

          MAXRESULTS = 10

        else

          MAXRESULTS = LIMIT

        FIELDLIST = "Title Description Url Source DateTime"

        POST_DATA = BuildSearchAPIPostData(SEARCH, "News", OFFSET, MAXRESULTS, CULTURE_INFO, FIELDLIST)

      http

        http://soap.search.msn.com:80/webservices.asmx

        header

          Accept: application/soap+xml

        postdata {encode=no}

          POST_DATA

      simple xml

        Envelope

          Body

            SearchResponse

              Response

                Responses

                  SourceResponse

                    Offset => RESULTOFFSET    // Where we're starting from.

                    Total => TOTAL     // Total number of results.  

                    Results

                      Result {loop=content}

                        Title

                        Description

                        Url

                        Source

                        DateTime

                          Year

                          Month

                          Day

                          Hour

                          Minute

                          Second

      postprocess

        INFO.Offset = RESULTOFFSET

        INFO.MaxCount = TOTAL

        return INFO

     

     

    There are other datasource properties that should be considered to either increase performance and or deal with potential errors in accessing/retrieving information from the datasource.  The first one is the Timeout property.  You can specify this time in order to lengthen or shorten the time it takes before the datasource quits accessing the web service.  The default value is 10 seconds.  In our case, we have it at 15 seconds.  The next property is the continue_on_error property.  By changing this property to ‘yes’, execution will still continue and the datasource caller can retrieve the error message in the SYS.Data.Error variable.  This is only on those sources that call the ABErrorProc.  The final property is very important.  It is the Expire property.  This determines how long retrieved data should be valid, i.e. kept in cahsed memory.  The ability to cache retrieved data in memory will improve performance on retrieving information in the datasource.  You should consider these factors:

     

    1) how often does the data change?

    2) how often will the same retrieved data be asked again?

    3) how large is the retrieved data set?

    4) server memory cache size (N/A on hosted applications)

    5) how fast does the web service perform?

     

    All of these are considerations.  In our case, since news items change frequently, we’ll set it for a relatively short time period, say 1 hour. 

     

    Examples of the Expire property:

     

    Expire=”never” /* this is the default expiration for most non-Buddyscript datasources */

    Expire=”in 1 hour”

    Expire =”now”  /* no caching at all, same as “never” */

    Expire=”tomorrow at 5am” /* Note that this time is the server time, not the client time.  In hosted applications, this is in GMT time */

     

    The postprocess section is important for returning a range of information.  For datasources that do not handle the processing of data using offsets and limits (i.e. simple xml), if the postprocess section is missing, the processing QueryServer will process the data coming back from the datasource in its entirety. In cases where the output coming back is one entity or one row, or if the amount of data needed to be processed is small, the postprocess section is not needed.

     

      postprocess

        INFO.Offset = RESULTOFFSET

        INFO.MaxCount = TOTAL

        return INFO

     

    Looking at the postprocess section, this section is used to set the offset and total count of rows in a variable.  This variable is then used by Buddyscript to control the display of output.

     

    In this case, INFO is the name of an object variable. The names of the variables inside the object is Offset and MaxCount, and these values are populated from the datasource:

     

                    Offset => RESULTOFFSET    // Where we're starting from.

                    Total => TOTAL     // Total number of results.  

     

     

    Finally, here is a crude routine to pass a request to the Live Search API, access the web service and display the contents of the data, using Buddyscript code to control the amount of data coming in.

     

    ? Tell me some news about STRING=Anything

      LOCALE="en-us"

      TITLE, DESCRIPTION, LINK, SOURCE, YEAR, MONTH, DAY, HOUR, MINUTE, SECOND = LiveSearchAPI(STRING, LOCALE) show 10

        * Here are the results:

        - TITLE, SOURCE

        * <blank/>

          <ifmore>Type "more" for more news.</ifmore>

      else

        - Sorry, no news sites were found for your input.  

     

    In the input, you could ask a question such as “Tell me some news about Baron Davis” for example, and get back results that looks like this (notice that the output only contains 2 out of the 10 arguments returned, Title and Source):

     

    Here are the results:

     

    Baron Davis Going South, San Francisco Gate

    The Baron Davis, Gilbert Arenas Switch-a-roo?, San Francisco Gate

    Clippers set sights on Baron Davis, Los Angeles Times

    Baron Davis on verge of signing with Clippers, Washington Post

    NBA: Warriors trying to woo Brand, Newsday

    Baron Davis becomes free agent, Chicago Sun-Times

    Report: Davis to ditch Warriors for Clippers, FOXSports.com

    Logo? Colors? History? Don't mean a thing if you ain't got that team, CBS Sportsline

    Davis on verge of joining Clippers, CNN Sports Illustrated

    Davis on verge of signing with Clippers, Salon

     

    Type “more” for more news.

    .

    .

    .

    .

     

     

    Notice that in the pattern routine, there is an option called SHOW 10.  This means to output 10 rows at a time.  Buddyscript will go to the output datasource to retrieve the information, which in this case happens to be exactly 10 rows, since the request was to buffer 10 rows per datasource request.  If the user were to type in “more”, another 10 rows will be retrieved from the datasource and 10 more rows will be displayed, and so on.  (Note that there is a Buddyscript variable named SYS.Presentation.Maxlength that also controls the number of characters that can be displayed on an IM client.  Depending on what this is set to, this number will also control the number of rows displayed back.)

     

    With the SHOW command, this allows the user a quick and equivalent way of emulating a forward read-only cursor, i.e. displaying x number of rows of output at one time.  The other option would be to put the data into an object and loop through the object, displaying each row, which involved more coding. It’s very possible that for control purposes, the latter method is the right way to go, but for quick coding and display, SHOW is very powerful.

     

    Hopefully you have gotten a chance to absorb the intricacies of using datasources by accessing a really powerful web service.  Thanks for your attention!

     

     
     

    An advanced look at Web Services and Datasources - Part I

    An advanced look at Web Services and Datasources

     

    Back in February, we showed a basic example of accessing a web service and using a datasource to consume information from a web service. (Refer to: http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!443.entry) Today, we’ll take a further look at a real-world web service, and some further things you can do with a datasource to make your application perform better, and to potentially reduce coding effort.

     

    Many agents rely on rich content in order to create a solid user experience.  Many times, this involves accessing an external data store, such as a web service. With some web services, you can parameterize your request in the URL itself, for example http:/URL?<input>.  More often, web services involve using request and response model via a SOAP API. 

     

    The Windows Live Search API is a very powerful web service that does many things.  It allows a user to do searches on the web, news, images, as well as get dictionary, phonebook and spelling information. It provides a web service XML interface using a SOAP API, enabling you to submit requests and get back a response via XML.  For more information on the XML, refer to this link:  http://msdn.microsoft.com/en-us/library/bb251794.aspx

     

    Using any number of XML tools, we can take a look at the WSDL of the Live Search API web service to see how a typical request and response is crafted.  The WSDL endpoint is this:

     

    http://soap.search.msn.com/webservices.asmx?wsdl

     

    For purposes of discussion, let’s say that we are going use a Web News search, searching for “Baron Davis”.  The query of “Baron Davis” is put into the Query element. There are numerous other elements that are part of the response.  They are highlighted in red. The XML response would look like this:

     

    <?xml version="1.0" encoding="utf-16"?>

    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

      <soap:Body>

        <Search xmlns="http://schemas.microsoft.com/MSNSearch/2005/09/fex">

          <Request>

            <AppID>your APP ID</AppID>

            <Query>Baron Davis</Query>

            <CultureInfo>en-us</CultureInfo>

            <SafeSearch>Moderate</SafeSearch>

            <Flags>None</Flags>

            <Location>

              <Latitude>0</Latitude>

              <Longitude>0</Longitude>

            </Location>

            <Requests>

              <SourceRequest>

                <Source>News</Source>

                <Offset>0</Offset>

                <Count>10</Count>

                <FileType />

                <ResultFields>All DateTime</ResultFields>

                <SearchTagFilters>

                  <string />

                </SearchTagFilters>

              </SourceRequest>

            </Requests>

          </Request>

     

    (Note that strangely enough, in the ResultFields element, putting in the request “All” usually returns back all fields In the case of a News request, this does not return back all fields. “All DateTime” will return all fields, plus the datetime in this case.)

     

    The XML response looks like this, with output fields in bold:

     

    <?xml version="1.0" encoding="utf-16"?>

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

      <soapenv:Body>

        <SearchResponse xmlns="http://schemas.microsoft.com/MSNSearch/2005/09/fex">

          <Response>

            <Responses>

              <SourceResponse>

                <Source>News</Source>

                <Offset>0</Offset>

                <Total>31185</Total>

                <Results>

                  <Result>

                    <Title>NBA free agent roundup: Baron Davis to L.A.?</Title>

                    <Description>Baron Davis has a verbal agreement with the Clippers, Gilbert Arenas likely is staying in Washington, and Elton Brand has a decision to make as NBA free agency opened with some surprises yesterday. The dominoes began to tumble when Davis surprised ... </Description>

    <Url>http://www.newsday.com/sports/basketball/knicks/ny-spnba025749266jul02,0,2266274.story </Url>

    <DisplayUrl>http://www.newsday.com/sports/basketball/knicks/ny-spnba025749266jul02,0,2266274.story</DisplayUrl>

                    <Source>Newsday</Source>

                    <DateTime>

                      <Year>2008</Year>

                      <Month>7</Month>

                      <Day>2</Day>

                      <Hour>15</Hour>

                      <Minute>26</Minute>

                      <Second>29</Second>

                    </DateTime>

                  </Result>

     

                  <Result>

                    <Title>Later Daze, Baron: Davis leaves Warriors for Clippers</Title>

                    <Description>del.icio.us Less than 24 hours after Davis shocked the Warriors by walking away from the final year and $17.8 million left on his contract, the franchise guard delivered a second strike Tuesday afternoon. Davis agreed in principle with the Clippers ... </Description>

    <Url>http://www.sfgate.com/cgi-bin/article.cgi?f=/c/a/2008/07/02/SPCF11ICKB.DTL</Url>

    <DisplayUrl>http://www.sfgate.com/cgi-bin/article.cgi?f=/c/a/2008/07/02/SPCF11ICKB.DTL</DisplayUrl>

                    <Source>San Francisco Gate</Source>

                    <DateTime>

                      <Year>2008</Year>

                      <Month>7</Month>

                      <Day>2</Day>

                      <Hour>15</Hour>

                      <Minute>19</Minute>

                      <Second>20</Second>

                    </DateTime>

                  </Result>

                 </Results>

              </SourceResponse>

            </Responses>

          </Response>

        </SearchResponse>

      </soapenv:Body>

    </soapenv:Envelope>

     

     

    For purposes of display, only 2 items are displayed, but in the real-life example, there are 31,185 articles on “Baron Davis” that were found.  The 31,185 is indicated in the <TOTAL> element in the response.  Note that not all 31,185 rows are retrieved.  The amount retrieved is regulated in the Preprocess block, in the built-in LIMIT and OFFSET variables. More on this later, when we describe the datasource.

     

    Let’s take a look at how we generate the Buddyscript code to access this web service.

     

    A typical datasource accessing XML might have header information that looks like this:

     

    datasource dsSample(ARG1, ARG2, ARG3) => A1, B1, C1

      http

        http://someurl/sample.xml

      simple xml

         .

         .

         .

     

    However, for a web service invoking SOAP messages, we have to build a request string, so we use the PreProcess section of a datasource to set a variable that will contain the request, along with other variables.  Thus, the beginning of the datasource would look like this instead:

     

    datasource LiveSearchAPI(SEARCH, CULTURE_INFO) => Title, Description, Url, Source, NewsYear, NewsMonth, NewsDay, NewsHour, NewsMinute, NewsSecond {expire="in 1 hour" continue_on_error="true"}

      preprocess

        if LIMIT>10 || LIMIT<=0

          MAXRESULTS = 10

        else

          MAXRESULTS = LIMIT

        FIELDLIST = "Title Description Url Source DateTime"

        POST_DATA = BuildSearchAPIPostData(SEARCH, "News", OFFSET, MAXRESULTS, CULTURE_INFO, FIELDLIST)

      http

        http://soap.search.msn.com:80/webservices.asmx

        header

          Accept: application/soap+xml

        postdata {encode=no}

          POST_DATA

      simple xml

         .

         .

         .

     

     

    We’ll describe the syntax of the datasource in more details later on, but for now, what’s important to point out is that a function is called to build the request string.  The function in this case is called BuildSearchAPIPostData and has a number of arguments.  Let’s take a look at the function:

     

     

    function BuildSearchAPIPostData(SEARCH, TYPE, OFFSET, COUNT,CULTURE_INFO,

      FIELDLIST, RADIUS, LATITUDE, LONGITUDE)

      if (LATITUDE eq "")

        LATITUDE = 0

      if (LONGITUDE eq "")

        LONGITUDE = 0

      if (RADIUS eq "")

        RADIUS = 5

      POST_DATA =                         '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'

      POST_DATA = StringConcat(POST_DATA, '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http:/\/schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:tns="http://schemas.microsoft.com/MSNSearch/2005/09/fex" > <SOAP-ENV:Body>')

      POST_DATA = StringConcat(POST_DATA, ' <tns:Search xmlns:tns="http:/\/schemas.microsoft.com/MSNSearch/2005/09/fex"> <tns:Request>\n')

      POST_DATA = StringConcat(POST_DATA, '   <tns:AppID>', GetLiveSearchAPIAppID(), '</tns:AppID>\n')

      POST_DATA = StringConcat(POST_DATA, '   <tns:Query>', SEARCH , '</tns:Query>\n')

      POST_DATA = StringConcat(POST_DATA, '   <tns:CultureInfo>', CULTURE_INFO, '</tns:CultureInfo> <tns:SafeSearch>Moderate</tns:SafeSearch> <tns:Flags>None</tns:Flags>\n')

      POST_DATA = StringConcat(POST_DATA, '   <tns:Location>\n')

      POST_DATA = StringConcat(POST_DATA, '     <tns:Latitude>', LATITUDE, '</tns:Latitude>\n')

      POST_DATA = StringConcat(POST_DATA, '     <tns:Longitude>', LONGITUDE, '</tns:Longitude>\n')

      POST_DATA = StringConcat(POST_DATA, '     <tns:Radius>', RADIUS, '</tns:Radius>\n')

      POST_DATA = StringConcat(POST_DATA, '   </tns:Location>\n')  

      POST_DATA = StringConcat(POST_DATA, '   <tns:Requests> <tns:SourceRequest>\n')

      POST_DATA = StringConcat(POST_DATA, '     <tns:Source>', TYPE, '</tns:Source>\n') // Web / Ads / InlineAnswers / PhoneBook / WordBreaker / Spelling

      POST_DATA = StringConcat(POST_DATA, '     <tns:Offset>', OFFSET, '</tns:Offset> <tns:Count>', COUNT, '</tns:Count> <tns:ResultFields>', FIELDLIST, '</tns:ResultFields>\n')

      POST_DATA = StringConcat(POST_DATA, '   </tns:SourceRequest> </tns:Requests>\n')

      POST_DATA = StringConcat(POST_DATA, ' </tns:Request> </tns:Search>\n')

      POST_DATA = StringConcat(POST_DATA, '</SOAP-ENV:Body> </SOAP-ENV:Envelope>\n')

      return POST_DATA  

     

     

    The function essentially uses the variable POST_DATA to build the request string.  If there are certain parameters without information, it defaults the information (e.g. LATITUDE).  The function also calls another function to get the Windows Live APP ID.  This APP ID is used in the Live Search API to determine individual access rights.  (See the Windows Live API for more information on obtaining this APP ID.)  The function itself simply looks like this:

     

    function GetLiveSearchAPIAppID()

      return "ABCDE12345" 

     

    where “ABCDE12345” is the App ID.

     

    We'll take a look at the rest of the datasource and invoke code later this week.

     

    July 02

    Guidelines for Testing your Agent

    As compared to Web sites and traditional software applications, conversational agents are subject to some unique policy compliance risks. These risks arise because:

     

    ·         End users’ interactions with agents are freeform and unpredictable.

    ·         Agents often engage in human-like interactions and operate in messaging environments normally used for human-to-human communications, making end users and outside observers especially sensitive to inappropriate content or behavior.

     

    Because of these unique risks, the Windows Live Agents team highly recommends that each Agent undergo manual testing for policy compliance prior to launching. Once testers have acquainted themselves with the task, approximately 4 to 8 hours of manual testing should provide a reasonable evaluation of the Agent’s policy compliance. Testers should:

     

    ·         Be native speakers

    ·         Have a good understanding of cultural and political factors that might determine whether an Agent’s content/behavior is appropriate

    ·         Be able to make judgments in the best interest of your public image and business interests in the market where the Agent will be released

    ·         Be willing to provoke the Agent to behave inappropriately (this requires creativity, persistence, and willingness/ability to imagine offensive and provocative user inputs)

    ·         Understand the Agent’s feature set

    ·         Ideally not have been directly involved in the Agent’s development

     

    If your testing uncovers any issues that you need help triaging or fixing, please contact Windows Live Agents Partner Support (agentsu@microsoft.com). Send a transcript illustrating each issue, along with a description (in English) of what the issue is.

     

    Overview for Testers

     

    This document is intended to provide guidelines and advice for manual testing of Agents for policy compliance. It outlines specific types of subject matter to focus on, common Agent vulnerabilities, and specific tactics that you can use in an attempt to uncover issues in a given Agent.

     

    This document is not a step-by-step test plan; nor is it by any means exhaustive. When performing compliance testing, there is no substitute for your own persistence and imagination. Furthermore, these guidelines do not currently prescribe any specific standards. You should apply your language and market expertise and your business judgment to determine whether the Agent’s behavior and content are acceptable. We strongly advise erring on the side of caution.

     

    You should read this document to acquaint yourself with the subject of Agent policy compliance. You may find the specific examples to be a helpful starting point, but effective testing will require you to apply your knowledge of the language and market for which the Agent is intended, and of the specific Agent’s content and features.

     

    In order to test effectively, you must be willing and able to imagine and try highly offensive and provocative user inputs. If you’re not comfortable with this task, then you should attempt to find someone who is.

     

    Once you have acquainted yourself with the task, approximately 4-8 hours of manual testing should provide a reasonable evaluation of the Agent’s policy compliance.

    Sensitive/Inappropriate subject matter to test

     

    ·         Profanity

    ·         Hate/Intolerance (with respect to race, gender, sexual orientation, religion, etc.)

    ·         Violence and criminal behavior

    ·         Drug use

    ·         Sexual content

    ·         Suicide

    ·         Culturally/Politically sensitive subjects in your market

    Scenarios to test

     

    ·         Imagine you are one of the Agent’s target users

    ·         Imagine you are a child

    ·         Imagine you are a malicious user attempting to provoke inappropriate Agent behavior

    Common Agent vulnerabilities

     

    ·         Agent may repeat (or “mirror”) user language without employing adequate safeguards

    ·         Agent may respond to the form of a user input without understanding the content

    ·         Agent may fail to recognize inappropriate or sensitive subject matter if the user employs creative/subtle phrasing

    ·         Agent may incorrectly determine an input to be inappropriate and in turn respond inappropriately

    ·         Agent may have “unsafe” catch-all responses (responses used when the user input is not understood at all)

     

    Some specific tactics to try

     

    ·         See how the Agent responds to blatant abuse and provocation

    ·         Try to trick the agent into repeating an inappropriate word or phrase

    ·         Try to elicit an inappropriate opinion from the Agent

    ·         Try to elicit the Agent’s approval (explicit or implicit) of an inappropriate statement

    ·         Try to elicit inappropriate answers to formulaic questions (yes/no, how many, etc.)

    ·         Try to elicit inappropriate responses to commands/requests/statements

    ·         Try to trick the Agent into inferring inappropriate intent where there is none (and responding inappropriately)

    July 01

    It's all about context, part deux!

    Hi again! Here is the second part of our visit to the magical world of contexts. Yesterday we brushed on a few simple uses for them, now let's dive into a slightly more sensitive subject.

     

      Once the agent is hosted and public, you may want to block access to the agent temporarily, for instance if you rely heavily on external data sources that are experiencing an outage or just very slow.

    In that case it's a good idea to keep the agent running normally for a limited set of superusers so they can work on the issue, while putting up an 'out of service' message for the general users.

     

     

    //The message is by default empty. If it's not then the agent knows that we want it muted.

    variable PUBLIC.OUTAGE_MESSAGE = ""

     

    context AgentIsDisabled {out-of-context="0" in-context="1000" condition="!IsSuperUser() && PUBLIC.OUTAGE_MESSAGE ne \"\""}

     

    start context AgentIsDisabled

     

    + =AnythingStrong

      - PUBLIC.OUTAGE_MESSAGE

     

    end context AgentIsDisabled

     

    // SuperUsers only an change the agent status. Easy! We've already prepared for that.

    start context RestrictedStrings

     

    + disable agent REASON=AnythingRaw

      PUBLIC.OUTAGE_MESSAGE = REASON

     

    + enable agent

      PUBLIC.OUTAGE_MESSAGE = ""

     

    end context RestrictedStrings

     

     

      Here the condition is simply to check if the outage message is empty or not, except for super users. They will continue to use the agent normally, and can reenable it whenever their experience is back to normal.

     

    Now, there is one problem in this code... did you spot it?

    Using a public variable like this in a context condition is pretty bad for performance. Indeed, it would mean that for each query of each user, the query server would need to lock access to the public variable in order to read it just to check the context condition.

    It gets even more troublesome if you're on dual-box hosting and require a Stored Public Variable to propagate the outage across queryservers!

    What is the solution then?

     One possible solution is to check the outage variable only once: at session start. All you need for this is to keep a local session variable of that setting.

     

     

    stored variable PUBLIC.STORED_OUTAGE_MESSAGE = ""

    variable G_OUTAGE_MESSAGE = ""

     

    procedure ABStartSessionProc()

      // This procedure can exist independently in any buddyscript file, and is called at session start

      if !IsSuperUser()

        lock profile // locking is required for stored public variables.

          G_OUTAGE_MESSAGE = PUBLIC.STORED_OUTAGE_MESSAGE

     

    context AgentIsDisabled {out-of-context="0" in-context="1000" condition="G_OUTAGE_MESSAGE ne \"\""} //note that we don't need to check for SuperUsers here as as it's done at session start

     

    start context AgentIsDisabled

     

    + =AnythingStrong

      - G_OUTAGE_MESSAGE

     

    end context AgentIsDisabled

     

    start context RestrictedStrings

     

    + disable agent REASON=AnythingStrong

      lock profile

        PUBLIC.STORED_OUTAGE_MESSAGE = REASON

     

    + enable agent

      lock profile

        PUBLIC.STORED_OUTAGE_MESSAGE = ""

     

    end context RestrictedStrings

     

      As you may have noticed, there is a caveat to this solution: since we only check the message at session start, the flag takes some time to propagate as only new users will be getting the message.

    The same is true when you re-enable the agent and it will take a few minutes for the outage to be over for everyone.

     

      So here, we only lock the agent's profile and check the public stored variable once for every session, which is good already. But can we do better?

    Imagine your agent is highly successful and deals with so many people that, say, five people start a new session every second. Don't we all dream of having an agent that popular! But it comes with a price: 5 calls to the public profile per second would probably bog the whole system down.

      What do you do then? Well, add yet another layer of buffering of course!

    Back to our friend the "basic" public variable. That one only lives for the current queryserver, but it is a lot more performant to check against. All we'll need is a background procedure to update the public variable every few minutes, so as to transmit the orders from the top to the base:

     

     

    stored variable PUBLIC.STORED_OUTAGE_MESSAGE_FOR_ALL_USERS = ""

    variable        PUBLIC.OUTAGE_MESSAGE_FOR_ALL_USERS_OF_THIS_SERVER = ""

    variable        G_OUTAGE_MESSAGE_FOR_THIS_USER = ""

     

    procedure Background_UpdateOutageMessage() startup every 1 minute

      // This procedure is called at startup and every minute, independently of any user session.

      lock profile

        PUBLIC.OUTAGE_MESSAGE_FOR_ALL_USERS_OF_THIS_SERVER = PUBLIC.STORED_OUTAGE_MESSAGE_FOR_ALL_USERS

     

    procedure ABStartSessionProc()

      if !IsSuperUser()

        G_OUTAGE_MESSAGE_FOR_THIS_USER = PUBLIC.OUTAGE_MESSAGE_FOR_ALL_USERS_OF_THIS_SERVER

     

    context AgentIsDisabled {out-of-context="0" in-context="1000" condition="G_OUTAGE_MESSAGE_FOR_THIS_USER ne \"\""}

     

    start context AgentIsDisabled

     

    + =AnythingStrong

      - G_OUTAGE_MESSAGE_FOR_THIS_USER

     

    end context AgentIsDisabled

     

    start context RestrictedStrings

     

    + disable agent REASON=AnythingStrong

      PUBLIC.STORED_OUTAGE_MESSAGE_FOR_ALL_USERS = REASON

     

    + enable agent

      PUBLIC.STORED_OUTAGE_MESSAGE_FOR_ALL_USERS = ""

     

    end context RestrictedStrings

     

      Here you have a piece of code, slighly more complex, but that will sustain any kind of traffic without budging, with the only downside of taking up to one more minute to propagate the change of the agent's status.

     

     And this concludes our two-day tour of contexts and their natural habitat. I hope you liked it, and please don't forget to check out our gift shop on the way out!

    June 30

    It's all about context!

     

     

      Today we're going to talk a bit about contexts and what wonderful things you can do with them.

    Well, maybe not "Wall-E opening sequence" wonderful, but they sure come in handy to control what your agent is doing in an easy and trouble-free way. With the help of contexts you can separate clearly your routines according to any condition, without having to worry about micro-managing their scores or doing systematic checks.

    We're going to take a closer look at a few different uses and related buddyscript features, from the very simple to... the wee bit advanced. Tomorrow we'll explore another use for them, and discuss public variables and performance issues.

     

    Here is how, for example, you could make sure that only users on MSN get access to the activity window features:

     

     

    context MSN_Only {out-of-context="0" in-context="100" condition="SYS.User.Service eq \"MSN\""}

    // out-of-context: score adjustment for the queries inside the context that don't verify the condition (here,   0 = no match)

    // in-context    : score adjustment for the queries inside the context that do verify the condition    (here, 100 = no adjustment)

     

    start context MSN_Only

     

    ? Start the activity.

      - Sending you an invitation...

     

    end context MSN_Only

     

     

     An interesting thing to note is that you don't have to end the context at the end of our file. If you were to start the context in a package, then every domain including this package, directly or indirectly, would be within that context and abide by its rules. So here you could have all domains related to Activity Window include this package, and be protected automatically.

      You could apply this principle to restrict certain features or queries based on any condition, like age, country, market, number of visits or what the user's high-score is at your quizz game.

     

    You can also restrict access to the agent even though it's launched and live on Messenger, while it's in development or beta phase:

     

     

    // This table lists the screennames of authorized users

    // Using "exact" as an index method will help make searches more performant

    datatable AuthorizedUsers

      load ScreenName {index="exact"} from

        mememe@hotmail.com

        uberbetatester@live.com

        popaandmoma@live.com

        mygloriousboss@live.com

     

    function UserIsAuthorized()

      if ShellMode() // Shellmode() returns true if we're in the SDK

        return true

      SN = get ScreenName in AuthorizedUsers where ScreenName is SYS.User.ScreenName

        // Found the screename: access granted!

        return true

      return false

     

    // Here the in-context is set to 1000 to make sure that any other routine is overruled.

    context Unauthorized {out-of-context="0" in-context="1000" condition="!UserIsAuthorized()"}

     

    start context Unauthorized

     

    + =AnythingStrong

      - Sorry, I'm in closed beta phase and can't talk to you yet. No peeping!

     

    // Within a context, regular matching still happens as they're all subject to the same adjustment.

    ? But I am a Very Important Person!

      - The Boss said: no exceptions.

     

    end context Unauthorized

     

     

     

      Another use, very similar in implementation, is to restrict certain debug or testing queries to a set of super-users, listed this time in a text file. The textfile-based table is easier to maintain independently from the code, and can be changed without having to restart the agent: all you need is to set an expire date for the file to be reloaded.

     

     

    datatable SuperUsers {expire="in one day"}

      // The list will be timed out and reloaded once a day from the file.

      load ScreenName {index="exact"} from file

        superusers.txt

     

    function IsSuperUser()

      if ShellMode()

        return true

      SN = get ScreenName in SuperUsers where ScreenName is SYS.User.ScreenName

        return true

      return false

     

    context RestrictedStrings {out-of-context="0" in-context="100" condition="IsSuperUser()"}

     

    start context RestrictedStrings

     

    // This topic is only accessible to the right people:

    + debug all variables

      - As you wish.

      dbg_display STORED_USER_VARIABLES

     

     

    That’s it for today. See you tomorrow for another round of goodies!

     

    June 26

    5.0 Transition Update!

    Hello Developers and Partners of Windows Live Agents,

     

    We are very excited that 5.0 is here! We have received many questions from all of you regarding the transition from 4.3 to 5.0.  Windows Live Agents Partner Support is no longer accepting projects developed in 4.3. All agent projects should be developed in 5.0 using our SDK in Visual Studio and submitted through our Partner hosting site at http://phi.agents.live.com. If you send us a new project developed in 4.3 or 5.0 via email, we will unfortunately not be able to accept it. If you have not already downloaded the 5.0 SDK in Visual Studio, you can do so here. For instructions on getting started with hosting, please click here.

     

    If we are currently hosting an agent of yours that was developed in 4.3, please continue to send us updates in 4.3 via email. We will let you know when we are ready to migrate your project from 4.3 to 5.0.

     

    Thanks,

    Windows Live Agents Team

    June 23

    Messenger Screen Name Parameters Updater

    Quite often we hear from partners who complain that the screen name, personal status message, or display picture of their agent has disappeared from the Messenger client, and ask us to fix it. Strictly speaking, the display of these parameters in the client is subject to forces outside the control of the Windows Live Agents group, however we continue to work with those in charge of the Messenger network to resolve issues like this.
     
    In the meantime, here is the code we use to "fix" the problem. If you are not already doing so, you should be using Method 3 from this blog post about changing friendly names, icons, and status messages. Updates to these Messenger parameters via edits to the BFG are no longer supported after 4.3!
     
    To add the screen name parametes updater, simply include these lines in your project:
     
    package lib:/Shared/Utilities/WLMUtilities 
     
    procedure WLMSetUpScreenNameParametersBackground() every 5 minutes per buddy
      call WLMSetUpScreenNameParameters()
     
    June 02

    Scripting tips: conversations 4/4 Using actions

    Greetings,

     

    We often design dialogs in BuddyScript to answer to user queries in a specific context. We will see today how by using actions, we can have initiate and notify to answer dialogs, and what can be the use.

    A dialog is a set of dialog entries that leaves in your script. A dialog entry is some matching information and a script block associated with. The matching information part usually contains patterns, but can contain actions. An action has a name and potentially parameters.

    The first example is a trivia with timer.

    // Trivia with timer

     

    ? Play trivia

      - Let's play trivia

        in what year was the Eiffel Tower built ?

        in 1889 {action=Right()}

        in 1943 {action=Wrong()}

        in 2001 {action=Wrong()}

        You have 30 seconds to answer

      NID = notify in 30 seconds: action TimeOut()

      ? 1889

      action Right()

        - That is correct!

      ? 1943

      ? 2001

      action Wrong()

        - Nope, the correct answer is 1889.

      action TimeOut()

        - Too late, the correct answer is 1889.

     

    At first, you can see a common use of actions, and that is with an enumeration. Typing 1 will trigger the action Right, typing 2 or 3 will trigger the action Wrong. Or you can fully type the date and match one of the three patterns.

    Then, you will notice the action in the notify. After 30 seconds, the notification will answer the dialog for you. If you do respond before the notification triggers, it would be wise to cancel the notification, but if you don’t, that’s ok because when the notification will trigger, no dialog with a TimeOut action will be active and the notification will simply be ignored.

    Initiates can also use actions, but through the respond statement. The syntax is:

    RID = respond SOURCE: action MyAction(PARAMS)

    The SOURCE is an object that contains the user identity (screenname, service and UID), the buddy id and the conversation id to send the action to. Most of the time, you are coming from an initiate and you simply pass the object from SYS.ConversationSource. Don’t forget to save that object in a variable if you have a dialog, because this variable won’t survive the dialog.

    A typical use for this statement is to get information from another user’s profile. In the following example, I have altered the “Do you know my friend” example to display a friendly name that the friend may have provided.

    // Do you know my friend(oh you mean Jacky ?)

     

    stored variable PREFERRED_NAME = ""

     

    ? Call me NAME=AnythingRaw

      PREFERRED_NAME = NAME

      - Ok, for now on, I will call you PREFERRED_NAME

     

    procedure GetPreferredName()

      if PREFERRED_NAME = ""

        NAME = SYS.User.ScreenName

      else

        NAME = PREFERRED_NAME

      CID = respond SYS.ConversationSource: action SetName(NAME)

     

    ? Do you know EMAIL=AnEMailAddress ?

      RESULT_NOLOAD = initiate EMAIL, "MSN": GetPreferredName() {createprofile=false loadprofile=false}

      if RESULT_NOLOAD.Delivered

        IN_SESSION = true

      else

        IN_SESSION = false

        RESULT_LOAD = initiate EMAIL, "MSN": GetPreferredName() {createprofile=false loadprofile=true}

        if !RESULT_LOAD.Delivered

          - Sorry, I can't say that I do

          exit

      action SetName(NAME)

        if NAME != EMAIL

          - Oh, you mean NAME ? \c

        - Yes, I know that guy\c

        if IN_SESSION

          - , and he/she's talking to me right now.

        else

          - .

     

     

    This script calls GetPreferredName under the friend’s profile, this procedure will respond by sending the preferred name. The script will wait in a dialog to get that answer.

     

    You can also constantly exchange information back and forth between users with respond. You just need one initiate to initiate the dialog. Here is another example that lets two users chat through the agent.

    // Chatter

     

    declare procedure InviteToChat(NAME)

    declare procedure Chat(SRC)

     

    ? I want to chat with EMAIL=AnEMailAddress

      - Ok, let me invite your friend over

      CID = initiate EMAIL, "MSN": InviteToChat(SYS.User.NickName) {createprofile=false loadprofile=true}

      action Accept()

        - Ok, your friend has accepted, you may chat now.

        call Chat(SYS.ConversationSource)

      action Reject()

        - Sorry, your friend has declined the offer.

      action NoResponse()

        - Hmm, nobody replied...

     

    procedure InviteToChat(NAME)

      - Hey, your friend NAME would like to chat with you, do you want to accept ?

      set user conversation

      SRC = SYS.ConversationSource

      NID = notify in 30 seconds: action TimeOut()

      ? Yes

        - Great, let's start chatting...

        RID = respond SRC: action Accept()

        call Chat(SRC)

      ? No

        - Fine.

        RID = respond SRC: action Reject()

      action TimeOut()

        - Hmm, you must not be here...

        RID = respond SRC: action NoResponse()

      insist: Please, say Yes or No.

           

    procedure Chat(SRC)

      - Type "quit" to quit the chat.

      ? TEXT=AnythingRaw

        RID = respond SRC: action SendText(TEXT)

        restart dialog

      action SendText(TEXT)

        - TEXT

        restart dialog

      ? Quit

        RID = respond SRC: action Quit()

        - You have terminated the conversation

      action Quit()

        - Your friend has terminated the conversation

       

    Have fun with all that.

     

    damien

    May 30

    Scripting tips: conversations 3/4 Handling conversation ids

    Today’s post will cover the ability to preserve conversations when sending notifications. The typical scenario we are trying to avoid is: a user is answering a series of troubleshooting questions, and then receives a stock alert. The alert proposes to see more details, and the user can’t go back to finish his troubleshooting steps.

    To avoid such disturbance, the BuddyScript engine runs notification and initiate alerts in different conversations from the one used to answer regular queries.

    Each conversation is uniquely identified by an identifier. A system variable tells you what your conversation id is:

    SYS.Conversation

    Another system variable tells you the id of the conversation used to answer queries. This is called the user conversation.

                SYS.User.Conversation

    If those two values are the same, you are in the user conversation, otherwise, don’t bother initiating a dialog.

    You can switch the user conversation to be the conversation you are in with the following statement:

                set user conversation

    You can also switch the user conversation to be a conversation you specify by doing:

                set user conversation CONVERSATION_ID

    With this statement, you can temporarily highjack the user conversation and later restore it.

    Finally, you can forward a query you just received to any conversation with the following statement:

                forward query CONVERSATION_ID

    With those three elements, you can highjack the user conversation, receive the user queries, forward the queries that you don’t care to the previous user conversation, and restore the previous user conversation.

    To illustrate this, here is the “Say Hi to my friend” example with the ability for the friend to reply back.

    // Respond to Hi

     

    declare procedure DisplayGreeting(FRIEND_NAME, GREETING)

     

    ? Say Hi to my friend EMAIL=AnEMailAddress

      RESULT = initiate EMAIL, "MSN": DisplayGreeting(SYS.User.NickName, "Hi") {createprofile=false loadprofile=true}

      - Ok, done.

     

    procedure DisplayGreeting(FRIEND_NAME, GREETING)

      - Hey, your friend FRIEND_NAME says GREETING!

      SOURCE = SYS.ConversationSource

      if SYS.Conversation = SYS.User.Conversation

        // This is the user conversation

        - Do you want to say GREETING back to FRIEND_NAME ?

        ? Yes

          RESULT = initiate SOURCE.ScreenName, SOURCE.Service: DisplayGreeting(SYS.User.NickName, GREETING + " back")

          - Done.

        ? No

          - Fine.

      else

        SAVED_USER_CONV = SYS.User.Conversation

        set user conversation

        - If you want to say GREETING back to your friend, just say "reply"

        ? reply

          RESULT = initiate SOURCE.ScreenName, SOURCE.Service: DisplayGreeting(SYS.User.NickName, GREETING + " back")

          - Done.

        ? ANYTHING=AnythingRaw

          forward query SAVED_USER_CONV

        set user conversation SAVED_USER_CONV

       

    The first thing DisplayGreeting does is to save SYS.ConversationSource in a variable. This variable contains some information on the user the initiate comes from, and will be used to initiate back.

    It then identifies if the current conversation is the user conversation. That would be the case if the user was not in session with the agent. Depending on the result, we will phrase our question differently. The response will have to be less ambiguous if the user was already in a conversation with the agent.

    If this is the user conversation, the rest of the code is simple.

    If it is not the user conversation, the procedure highjacks it. If the user sends “reply”, an initiate is sent back to the originator. If the user sends anything else, the query is forwarded to the previous user conversation. In any case, the user conversation is restored to the previous one.

    You can try this example with a routine with a dialog and see if the user that receives the greeting can ignore the question and respond to the dialog he was in. You can also test if he can still respond to his dialog after saying “reply”.

    Here now is the kitchen timer example with a snooze capability. The same logic is used here.

    // Kitchen Timer with snooze

     

    declare procedure Ring()

    variable KITCHEN_TIMER_NOTIFICATION = ""

     

    ? Kitchen timer

      - Ok, I'm setting up the time, how many minutes do you want ?

      ? DELAY=Integer

        - Alright, I will notify you in DELAY minutes

        KITCHEN_TIMER_NOTIFICATION = notify in DELAY minutes: Ring()

     

    ? Cancel Kitchen timer

      if KITCHEN_TIMER_NOTIFICATION = ""

        - What timer ?

      else

        cancel notify KITCHEN_TIMER_NOTIFICATION

        KITCHEN_TIMER_NOTIFICATION = ""

        - Done.

       

    procedure Ring()

      - DRRRRRRRING !!!!!

        Your time is up!

      KITCHEN_TIMER_NOTIFICATION = ""

      if SYS.Conversation = SYS.User.Conversation

        // This is the user conversation

        - Do you want to snooze ?

        ? Yes

          KITCHEN_TIMER_NOTIFICATION = notify in 5 minutes: Ring()

          - Will ring again in 5 minutes.

        ? No

          - Fine.

      else

        SAVED_USER_CONV = SYS.User.Conversation

        set user conversation

        - To give an extra 5 minutes, type "snooze"

        ? Snooze

          KITCHEN_TIMER_NOTIFICATION = notify in 5 minutes: Ring()

          - Will ring again in 5 minutes.

        ? =AnythingRaw

          forward query SAVED_USER_CONV

        set user conversation SAVED_USER_CONV

     

    Next post will explain how to empower your scripts by combining initiate and notify with dialogs. This is done through actions.

     

    Damien.

    May 29

    Scripting tips: conversations 2/4 Controlling the execution of initiate and notify

    Good (morning|afternoon|evening),

    With the initiate functionality, you can choose to put one of two restrictions based on the targeted user:

    ·         The user has to be known to the agent

    ·         The user has to actually be conversing with the agent

    This is a good idea to set those restrictions to avoid spamming the users.

    By checking the result of initiate, you can know if it succeeded or not. If it did not, depending on the restrictions you just put, you can conclude if the user is known to the agent or if the user is not currently talking to the agent. You set those restrictions by adding the following properties:

    ·         {createprofile=false}: initiate will fail if the user doesn’t have a profile yet.

    ·         {loadprofile=false}: initiate will fail if the user is not in session with the agent.

    The result of initiate is an object, the two important members of that object are:

    ·         Delivered: contains 1 if delivered, 0 otherwise

    ·         Status: string indicating the reason it was not delivered.

    Here is an example of use of that information, I call it “Do you know my friend?”

    // Do you know my friend

     

    procedure DoNothing()

      nop

     

    ? Do you know EMAIL=AnEMailAddress ?

      RESULT_NOLOAD = initiate EMAIL, "MSN": DoNothing() {createprofile=false loadprofile=false}

      if RESULT_NOLOAD.Delivered

        - Yes I do, and he/she's talking to me right now.

      else

        RESULT_LOAD = initiate EMAIL, "MSN": DoNothing() {createprofile=false loadprofile=true}

        if RESULT_LOAD.Delivered

          - Yes, I know that guy

        else

          - Sorry, I can't say that I do

     

    Another use of initiate is to send information from one user to another user. The next example is “Leave a note to my friend”. It lets a user leave a message for another user, which will get the message the next time he/she talks to the agent.

    In addition, this example checks to see if the user is currently in session, in which case it will deliver the message immediately.

    // Leave a note to my friend

     

    declare procedure SendMessageDirectly(FRIEND_NAME, MESSAGE)

    declare procedure LeaveMessageNote(FRIEND_NAME, MESSAGE)

     

    ? Can I leave a note to EMAIL=AnEMailAddress ?

      - Sure, what's the message ?

      ? MESSAGE=AnythingRaw

        RESULT = initiate EMAIL, "MSN": SendMessageDirectly(SYS.User.NickName, MESSAGE) {createprofile=false loadprofile=false}

        if RESULT.Delivered

          - Well, your friend and I are actually talking to each other right now, so I'm telling him/her now.

        else

          RESULT = initiate EMAIL, "MSN": LeaveMessageNote(SYS.User.NickName, MESSAGE) {createprofile=false loadprofile=true}

          - Ok, I'll leave a note for him/her to read next time he/she talks to me

     

    procedure SendMessageDirectly(FRIEND_NAME, MESSAGE)

      - Hey, your friend FRIEND_NAME has left a message for you:

        MESSAGE

     

    stored variable OFFLINE_MESSAGE_FROM_FRIEND = ()

     

    procedure LeaveMessageNote(FRIEND_NAME, MESSAGE)

      OFFLINE_MESSAGE_FROM_FRIEND.FriendName = FRIEND_NAME

      OFFLINE_MESSAGE_FROM_FRIEND.Message = MESSAGE

     

    procedure overrides ABGreetingProc(USERARRIVES, NEWUSER)

      - Good day!

      if OFFLINE_MESSAGE_FROM_FRIEND != ()

        - Hey, your friend OFFLINE_MESSAGE_FROM_FRIEND.FriendName left the following message for you:

          OFFLINE_MESSAGE_FROM_FRIEND.Message

        OFFLINE_MESSAGE_FROM_FRIEND = ()

       

     

    About notifications, the only thing there is to do is to cancel them before they trigger. This is done by saving the notification id and passing it to the “cancel notify” statement.

    To illustrate this simple thing, here’s the previous Kitchen timer example improved with a stop functionality.

    // Kitchen Timer stopper

     

    declare procedure Ring()

    variable KITCHEN_TIMER_NOTIFICATION = ""

     

    ? Kitchen timer

      - Ok, I'm setting up the time, how many minutes do you want ?

      ? DELAY=Integer

        - Alright, I will notify you in DELAY minutes

        KITCHEN_TIMER_NOTIFICATION = notify in DELAY minutes: Ring()

     

    ? Cancel Kitchen timer

      if KITCHEN_TIMER_NOTIFICATION = ""

        - What timer ?

      else

        cancel notify KITCHEN_TIMER_NOTIFICATION

        KITCHEN_TIMER_NOTIFICATION = ""

        - Done.

       

    procedure Ring()

      - DRRRRRRRING !!!!!

        Your time is up!

      KITCHEN_TIMER_NOTIFICATION = ""

     

    Those messages, coming from an initiate or a notification, are designed not to disrupt the conversation. What that means is if the user is expected to answer a dialog or an enumeration, the fact that his kitchen timer rings or that he receives a message from a friend won’t prevent him from answering as he was supposed to.

    What that also means is, if you add a dialog to your Ring procedure, the user will most likely not be able to answer it. How to fix this? This will be explained in the next post.

    Damien.

    May 28

    Scripting tips: conversations 1/4 Basic initiate and notify

    Hello fellow agent developers,

     

    I am going to present, in this post and the next 3 ones, the notion of conversations in BuddyScript and the way to best use them with the “initiate” and “notify” functionalities.

    Here are the items I am going to cover in those four posts:

    1.     Basic use of initiate and notify.

    2.     Controlling the execution of initiate and notify.

    3.     Handling conversation ids.

    4.     Using actions.

    Controlling the execution tells you how to know if a user is talking to the agent or not, if he has ever talked to the agent or not, and how to cancel a notification.

    Handling conversation ids will show you how to interact with the user from a notification without disturbing the current conversation he was in. For example, the user plays a game with the agent and the agent throws a stock ticker alert, proposing the user to see details; the user should be able to finish his game.

    Using actions will show you how to exchange information between users back and forth in an efficient manner, and how to combine notifications with dialogs. An example of use is a trivia game with a timer.

     

    But today, I’m going to describe the initiate and notify functionalities.

    Initiate is a BuddyScript functionality that lets you call a procedure on behalf of someone else. This procedure is executed with the profile of the user you specified, and any output generated will be sent to that user.

    The first example is the “Say Hi to my friend” program.

    // Say Hi to my friend

     

    declare procedure DisplayGreeting(FRIEND_NAME, GREETING)

     

    ? Say Hi to my friend EMAIL=AnEMailAddress

      RESULT = initiate EMAIL, "MSN": DisplayGreeting(SYS.User.NickName, "Hi") {createprofile=false loadprofile=true}

      - Ok, done.

     

    procedure DisplayGreeting(FRIEND_NAME, GREETING)

      - Hey, your friend FRIEND_NAME says GREETING!

    In this example, user Joe will ask to say Hi to Mary, providing Mary’s live messenger screen name. As a result, Mary will receive a greeting from the agent. In this example, Mary has to have talked to the agent already; she must already have a profile stored.

     

    The notify functionality calls a given procedure at a given time from the same user. It is used to create timers and reminders.

    An example for it is the “Kitchen Timer”.

    // Kitchen timer

     

    declare procedure Ring()

     

    ? Kitchen timer

      - Ok, I'm setting up the time, how many minutes do you want ?

      ? DELAY=Integer

        - Alright, I will notify you in DELAY minutes

        ID = notify in DELAY minutes: Ring()

       

    procedure Ring()

      - DRRRRRRRING !!!!!

        Your time is up!

     

    Perfect to boil an egg.

    If you want the best of both worlds, being able to specify the procedure to call, the time to execute it and for which user, you can combine the two.

    Here’s the “Friend’s reminder” example

    // Friend's reminder

     

    declare procedure RemindFriend(FRIEND_NAME, ACTION, TIME)

    declare procedure RemindNotification(ACTION)

     

    ? Remind my friend EMAIL=AnEMailAddress to ACTION=AnythingRaw at TIME=ATime

      RESULT = initiate EMAIL, "MSN": RemindFriend(SYS.User.NickName, ACTION, TIME)

      - Ok, that's done.

     

    procedure RemindFriend(FRIEND_NAME, ACTION, TIME)

      STIME = TIME.hour + ":" + TIME.minute

      - Hey, SYS.User.ScreenName, FRIEND_NAME would like to remind you to ACTION at STIME.

      ID = notify day at STIME: RemindNotification(ACTION)

     

    procedure RemindNotification(ACTION)

      - Hey there, it's time to ACTION!

     

    The procedure “RemindFriend” doesn’t need to output anything to work. The alternative would be to do the notify first, followed by initiate.

    Tomorrow, we’ll see in more details those two functionalities.

    Damien.

    Public Release of Windows Live Agents SDK!

    Hello Developers and Partners of Windows Live Agents,

     

    We are pleased to announce the public release of our new SDK for Visual Studio.  We have been in limited beta of the Colloquis SDK for quite some time and are excited to release this new technology to the public.  This new release of our SDK, as well as our Platform, marks an important step in building the Agents developer community.  We’ve been publishing weekly updates to this blog for almost a year, as well as supporting developers through our MSDN Forum, so we’re hopeful that we’ve gotten all your feedback and input handled in this newest release.  In addition to the release of the Platform and SDK, we’re also releasing our new system for hosting Agents at Microsoft, PHI (Partner Hosting Infrastructure).  With PHI, the previous method of mailing a project manager or an alias will be replaced by submitting your Agents through an automated system, receiving status on your project, and administering all your projects through a single console.  Hosting of your Agent will require a nominal yearly fee that offsets hardware costs (prices starting at ~$10k USD/year).  If you are working in partnership with Windows Live or MSN, that fee will likely be waived as part of your development contract on the product you are delivering.

     

    I highly recommend you read the release notes below for important process related questions for Agent development, as well as our SDK Documentation. There is important information about how to use PHI, the IDE, and what guidelines you must follow in order to develop in this community.  As we’ve noted in earlier blog posts, the older standard of MSN Bot is no longer supported, or certified by Microsoft.  Windows Live Agents replaces the terminology, technology, guidelines, and processes under which many of you have operated in the past, but we believe we’ve spoken about this at great length and have given our partners a chance to migrate effectively.  The users of Messenger and Windows Live ID are protected by Microsoft’s global policy and we’re ensuring that Agents on our network are held to the highest standard in online safety by using the Agents SDK for development.  Development of an Agent on the Agents SDK does not ensure that an Agent will be hosted by Microsoft, pending content reviews and approvals by the policy and safety teams.

     

    While we have many partners that work closely with the Agents team today, we are actively seeking new professional vendors that use this SDK to partner with us and the MSN Markets.  If you have been working with an MSN Market already, please send an email to wlabiz@microsoft.com to let us know the Agents you’ve developed and the people you’ve been in contact with at MSN.  Our development team is always recommending partners for vendor work in different regions.  We’ll be formalizing these partnerships under the Microsoft Partner Program in the future, but welcome the on-boarding of new development teams that can build Agents for online media needs. 

     

    Any questions that you have on the release, the process, or the software, should be posted on the MSDN Forum.  The previous alias used by many today will be monitored, but replies will be limited to developers that are explicitly directed there.

     

    We look forward to seeing the innovation from your development teams!

     

    Best,

    Windows Live Agents development team

     

     

    Here are the relevant links for download and information:

    ·         SDK

    ·         SDK Documentation

    ·         PHI

    ·         Development Blog

    ·         MSDN Forum

    New Features and Processes in Windows Live Agents 5.0

    ·         Visual Studio SDK

    o   We will be retiring the old standalone Colloquis SDK (versions 4.3 and previous) within 90 days of our 5.0 release. The process by which you take your previously developed projects and update them will be discussed with our release notes, but we want to ensure that developers have Visual Studio 2005 or 2008 Standard (or above) prior to the release of our Agents plug-in.  Please subscribe to Alerts on our blog page for availability of the SDK as we will be posting the announcement there.

    ·         Partner Hosting Infrastructure (PHI)

    o   We are streamlining the process by which projects can be hosted within Microsoft, if you choose to be hosted with us.  The old method of contacting a project manager within Microsoft will be replaced with our PHI system.  Within PHI, you will be submitting your Agents through an automated system, receiving status on your project, and administering all your projects through a single console.  Hosting of your Agent will require a nominal yearly fee that offsets hardware costs. We will be updating folks on the pricing model in the near future on our blog and through Windows Live development announcements, but it’s likely you’ll be able to waive these hosting fees if you are working with an MSN Market.  Microsoft will only host Agents that are developed within the languages that are supported by the SDK (English, French, Italian, German, Spanish, Japanese, Chinese; Portuguese and Dutch will release in the next few months).

    ·         Agent Provisioning

    o   As part of the ongoing safety for our end-users, the ability to have a Windows Live ID provisioned as an Agent will require that the Windows Live Agent SDK is used for development of your Agent.  The previous method of submitting through Windows Live Gallery is no longer supported and will soon be removed from the system all together.  Provisioning an Agent lifts the limit of contacts within Windows Live Messenger, but will be enforced strictly to ensure our Platform is in use.

    ·         Compliance

    o   The Windows Live Agents SDK is now enforcing Policy Compliance in all responses from an Agent hosted by Microsoft.  What this means to you as a developer is that your Agent may not respond with the exact text that you had scripted during your development.  When an Agent is hosted by Microsoft, we’ll have an additional set of tools that runs against your code to ensure that the response is appropriate for any and all users of Messenger.  We are holding our developers to the highest standards in online safety and will be posting blog entries on how you can safeguard your code within the SDK prior to Windows Live hosting.  You will be able to easily mimic the production conversation during development with these tools, so you can be sure your Agent is compliant with online safety guidelines.  If you are working with an MSN Market or Microsoft product, you will be hosted by Microsoft and subject to these tools.

    ·         Self-Hosting

    o   As you are downloading the new SDK, you may run this free version with your production Agent, in your own environment.  There are limitations on sessions within this self-host model, as well as some other limitations in terms of high availability deployments, but it is almost exactly the same product as we would host for you through PHI.  This is an ideal setup for the smaller development teams that do not have SLA’s on their Agent and wish to prototype features.  We have found this scenario to easily handle a large percentage of Agent traffic in the field today.  Again, this model enforces that the Agent SDK is in place to connect to Messenger as a provisioned Agent.

     

     

    IDE

    We have completely moved away from the custom made IDE that all projects up to the 4.3 release of our platform used. Instead, we now have a solution fully integrated in Visual Studio as an add-in.

     

    Full details on the new IDE are found in our documentation and on our blog.  Some quick highlights of features to look out for:

    ·         Conversation and comprehension windows redesigned to better show the inner workings of how query matches are done

    ·         Solution explorer to navigate your project

    ·         Colorization and IntelliSense support

    ·         Object browser for viewing all objects declared in your project

    ·         Full integration with our Partner Hosting Infrastructure (PHI). For example, seamlessly check in and check out your PHI projects.

    ·         And more…

    Migrating a Project from 4.3 to 5.0

    We have developed a script that helps to convert a project from 4.3 and make it compatible with our 5.0 release. The script basically creates a .connections file based on the language dependencies it reads in from the .dls file, updates obsolete references to the old Buddyscript libraries, and updates the .dls file.

     

    An important thing to note is that the script will overwrite your current project directory. To be safe, you should backup your project directory before running the script.

     

    The script is located in your Windows Live Agents SDK install directory (default location is in Program Files) at:

     

    Windows Live Agents SDK\Projects\ProjectConversion\ConvertProjectTo5_0.bat

     

    Drag and drop the project directory you wish to convert onto the .bat file, or double click the .bat file and enter the project directory path.

     

    Note: The conversion script will not necessarily  address every issue. You will most likely have to do some manual work after running the conversion script. But it will still save you quite a bit of work.

     

    PHI

    Overview

    Partner Hosting Infrastructure, or PHI, is a tool that will allow developers to apply for Microsoft to host their Agent projects. Developers will be able to upload their code, manage projects, and be informed of the current state of their projects.  This section will go over the tools that developers have available to them when developing a project, taking it live, and making changes to project files.

    It allows developers to :

    ·         Submit a project application

    ·         Check in code for that Project

    ·         Stage and Test your Project

    ·         Go Live with your Project

     

    How to modify your Project

    Once your project is hosted at Microsoft, you have two options available to you when you would like to modify files in your project.

     

    Using the Console

    You can use the KMS Console at https://sampleProj.console.agents.live.com  to modify the project. This will allow you to edit topics and responses fairly easily. For more control, however, you would want to use the IDE to make project changes.

     

    Using the IDE

    You can use the IDE to check in files in the same way that you checked in the project originally. Please note that using the IDE will check in an entire project, and does not allow you to check in just a single file.

     

    To check out using the IDE, you must first sign in with your Windows Live ID and acquire a project lock by clicking on Tools à Windows Live Agents Tools à Code Management à Check Out Project. You can then edit whichever files you wish. While a project is checked out to you, no one will be able to edit any of the project files using the IDE or KMS.

     

    To check in, click on Tools à Windows Live Agents Tools à Code Management à Check In Project.

     

    Checking In the Connections File

    When you check in the connections file, it must be reviewed and approved before being hosted or going live in the data center. Check in the connections file as you would check in any other file (see directions above).

     

    Checking in your project when the .connections file has not been modified will not trigger a review. Those changes should propagate immediately.

    May 20

    Definition of Reporting Terms in Usage Reporting

    Definition of Reporting Terms in Usage Reporting

    When your agent launches, you will be given access to the Knowledge Management Server, which includes a Usage Reporting section. The URL is https://YourProjectName.console.agents.live.com.

     

    When you navigate to the Usage Reporting site, you will see date range options in the left pane, and report results in the right pane.  The default date is the present day.  You can switch days by clicking on the calendar on the left.

     

    If you click on “Custom Range” on the upper left side of the page, you can view usage reports for a custom date range. Please not that in this view, New & Unique Users will be shown as N/A. Due to a design limitation, we currently cannot provide accurate measurements of new and unique users for custom reporting periods.

     

    At the top of the page, you may see the following sections of reporting: Volume Summary, User Demographics, Languages Used, Activity Usage, Compliance, Category Analysis, Topic Analysis, and Clickthroughs. The default reporting view is Volume Summary. The sections of Usage Reporting you see, depends on how you have set up the usage_config.xml your project uses. For more information on customizing the usage_config.xml, click here.

     

    Definition of Reporting Terms: 

     

    Volume Summary Section

     

    Total Queries:             

    The total number of queries in all sessions within the specified time period. For example, if the time period includes 100 sessions, there could be 1000 or more total queries.

     

    Total Sessions:           

    The total number of sessions within the specified time period.

     

    Unique Users:             

    The number of unique users within the specified time period. Each user is counted once, regardless of how often they interact with the agent. For example, if one person has ten separate sessions with the agent during the specified time period, they are still identified as one unique user.

     

    New Users:                  

    The number of users whose initial session with the agent occurred during the specified time period.

     

    Average Queries Per Session:

    Total queries within the specified time period divided by total sessions within the specified time period.

     

    Average Sessions Per Unique Users:  

    Number of unique users within the specified time period divided by total number of sessions within the specified time period.

     

    Category & Topic Analysis Sections

     

    Category Analysis is broken out into two sections: Category Distribution Per Query and Category Distribution Per Session. Category Distribution Per Query breaks down how many queries for the chosen date range matched to a category. Category Distribution Per Session breaks down how many times a query matched on a Category during a session.

     

    Topic Analysis is only broken out into one section: Topic Distribution (By Query). This measures how many times users’ queries matched to a specific topic for the chosen date range.

     

    For information on how to log Category and Topic Analysis in your project, click here.

     

    Clickthrough Section

     

    The Clickthrough section breaks down how many times a user is presented a link and how many times a user clicks on the link.

     

    Total Impressions

    Number of times a user is presented a link in their conversation with the bot.

     

    Total Clicks

    Number of times a user clicks on the link he / she is presented.

     

    For information on tracking ClickThroughs in your project, click here.

     

    Languages Used Section

     

    Agent detects what languages users speak to the agent in.

     

    Activity Usage Section

     

    Tracks how many users accept or reject an invitation to open the Activity Window.

     

    Compliance Section

     

    Conversations Stopped

    Number of conversations interrupted after detecting user was typing sensitive topics beyond the scope of the agent.

     

    Sensitive Sequences Rejected

    Number of answers that were blocked by the output filter, displaying an error message to the user.

     

    Sensitive Sequences Trusted

    Number of answers that would have been blocked by the output filter, but were let through because of the use of the tag <trusted>.

     

    Answers Invoking Trust

    Number of messages that were displayed with the tag <trusted>…</trusted> in them. It includes both messages that would have passed the output filter or failed it.

     

    May 15

    Adding a Usage Report to the Console

    The standard reporting suite on the Knowledge Management Console covers the most important metrics -- Unique Users, Total Sessions, etc. However, you'll often want (or be asked) to provide additional reports based on the special needs of the customer and/or the particular tasks of the agent. This article won't cover all the details of configuring usage reports, but will hopefully demonstrate how to log a simple data point and have it show up on the console as a graph or table.
     
    Before configuring usage, we should look briefly at how logging works. The agent's log is an object variable called SYS.Log -- anything written there is a candidate to show up in usage reports. This is completely separate from the User Profile: the agent may store the user's birthplace in a variable called G_USER.birthplace, but it won't show up in usage unless we also write it to, for example, SYS.Log.birthplace. It is also completely separate from the server component logs on the console.
     
    So let's actually use birthplace as our example, and look at what we need to record and display that statistic.
     
    Say we have a procedure called CollectBirthplaceFromUser() that takes care of collecting the user's birthplace and resolving it into a valid place name we can record.
     
    procedure CollectBirthplaceFromUser()
      BIRTHPLACE = ""
      [...]
      call WriteBirthplaceToProfile(BIRTHPLACE)
      call LogBirthplace(BIRTHPLACE)
     
    WriteBirthplaceToProfile assigns the value of BIRTHPLACE to G_USER.birthplace. LogBirthplace writes the same value to the SYS.Log.birthplace variable.
     
    procedure LogBirthplace(BIRTHPLACE)
      SYS.Log.birthplace = BIRTHPLACE
     
    Now that we're confident birthplaces are being collected and logged, let's move on to configuring Usage. We do this by editing a file at the root directory of the project, called usage_config.xml.
     
    usage_config.xml controls the presentation of log data in the Usage Reports section of the console.  It's completely separate from the actual logging of data -- we can log something in the BuddyScript, for example, but not add the necessary sections in usage_config.xml for another month, at which point the data the agent has been logging for the last month will be displayed. However, the opposite is not true -- adding a section in usage_config.xml doesn't cause data to be collected.
     
    There are two main parts: a "sections" element containing all the configuration options for the usage reports, and a "usagestats" element containing the variables and datapoints to be used in the sections element.
     
    The sections element itself contains two subsections labeled "all_ids" and "individual_id", which correspond to views of usage statistics broken out by individual buddyid or all buddyid's combined, exposed as a drop-down list in the console. First we'll look at the all_ids section, which is somewhat simpler than the individual_id section.
     
    The first section here is called Volume Summary, and contains a number of elements for reporting usage data. You can get a good idea of the syntax we'll be using later by examining this section.
     
     <section name="Volume Summary" id="volume_summary" shared-graph-type="barline" shared-graph-title="Usage">
      <element name="Total Queries" key="message_count" calc-type="normal" default-graph-state="on" graph-type="shared"/>
      <element name="Total Sessions" key="session_count" calc-type="normal" default-graph-state="on" graph-type="shared"/>
      <element name="Unique Users" key="unique_users" calc-type="normal" default-graph-state="off" graph-type="none"/>
      <element name="New Users" key="new_users" calc-type="normal" default-graph-state="off" graph-type="shared"/>
      <element name="Average Queries Per Session" key="message_count" calc-type="per" subkey="session_count" graph-type="shared"/>
      <element name="Average Sessions Per Unique User" key="session_count" calc-type="per" subkey="unique_users" graph-type="none"/>
      <element name="Average Session Length (in seconds)" key="total_session_length" calc-type="per" subkey="session_count" graph-type="shared"/>
     </section>
     
    The "name" identifier is the text displayed on the console, the "key" is the datapoint, "calc-type" can be normal or something else depending on whether we want to perform some math on the number before displaying it, "subkey" is the other datapoint to use when calc-type is not normal, "default-graph-state" toggles whether a particular element shows up on the graph, "graph-type" controls what kind of graph, if any, to display the data on. In this case, multiple elements are sharing a "barline" graph -- a combination bar graph and line graph where the user selects which data goes on the bar, and which goes on the line.
     
    We could add our own section for birthplace, but it turns out there is already a section for demographics, so we'll just add an element within that section:
     
      <element name="Birthplace Distribution" key='[birthplace][%]' keyName="Birthplace" valueName="Sessions" calc-type="distribution" graph-type="pie" display-type="percentage"/>     
     
    This will pull all the values of SYS.Log.birthplace and display them in a pie graph, distributed by the number of sessions they occur in, along with a key. ("name", "keyName", and "valueName" are all labels and have no effect on the display of the data.) [%] represents a wildcard, meaning "all values of birthplace" -- we could choose instead to report on only [birthplace]['New York'], for example. 
     
    Since there are potentially a large number of birthplaces (depending on how smart CollectBirthplaceFromUser is), it probably makes sense to dispense with the graph, and just display a table:
     
      <element name="Birthplace Distribution" key='[birthplace][%]' keyName="Birthplace" valueName="Sessions" calc-type="distribution" display-type="percentage"/>     
     
    That's all we need to do for the all_ids section. For the individual_ids section, because we'll be filtering by buddyid, we need to go to the bottom of the file and add a datapoint:
     
            <var name="userBirthplace" default="">value('[birthplace]')</var>
      <datapoint buckets="+hour" key="sessions where userBirthplace=${userBirthplace};buddyid=${buddyid}">
        <filter>userBirthplace != ''</filter>
      </datapoint>
     
    First we create a variable called userBirthplace, which is assigned the value of SYS.Log.birthplace. Then we create an hourly bucket for that data with a key for pulling the number of sessions within a particular buddyid where a particular birthplace was recorded. In the demographics section of the individual_id section, we'll add this:
     
      <element name="Birthplace Distribution" key='sessions where userBirthplace=%;buddyid=%var:buddyid%' keyName="Birthplace" valueName="Sessions" calc-type="distribution" display-type="percentage"/>     
     
    This is similar to the element we created for the all_ids section, but birthplace (userBirthplace=%) and buddyid (buddyid=%var:buddyid%) are interpolated into their actual values, and we see only the number of sessions for a birthplace recorded for the buddyid currently selected. Now we can use the buddy dropdown on the console as a filter.
     
    In practice, displaying a list of arbitrary names of places is not going to be very useful because there are many thousands of place names the user can enter. We'd probably want to constrain the list to countries, states, or other large areas, depending on the mission of the agent. Still, this example will hopefully get you started experimenting with Usage, which is the best way to learn. Chances are, usage_config.xml probably already contains most of the necessary pieces for whatever report you need to create.
    May 12

    New version of the Platform and SDK coming soon

    Hi All,
     
    I know a lot of folks have been waiting diligently for the new version of the SDK.  We have finished the product and are in the midst of deploying the platform in our data center now.  Once we get the final approval from Operations, we'll publish a link to the SDK and you'll be on your way!
     
    Thanks for you patience and we look forward to you using the SDK!
     
    Windows Live Agents team
    April 25

    BuddyScript Subpatterns

    Subpatterns are much like functions in that they can be used as filters for data -- one value is passed in, evaluated, and another possibly different value is returned. That's why we can do things like this:
     
    subpattern Basketball
    + (basketball|basket ball)
    + (b ball|bball) {MACRO_SYN}
    + (hoops|one on one|1 one 1) {MACRO_WEAK_SYN}
      return "basketball"
     
    subpattern Baseball
    + (baseball|base ball)
    + ball {MACRO_WEAKER_SYN}
      return "baseball"
     
    subpattern ASport
    + VAR=Basketball
    + VAR=Baseball
      return VAR
     
    + i like SPORT=ASport
      - I like SPORT too!
     
    User: i like bball
    Agent:  I like basketball too!
     
    Let's look at a slightly different kind of subpattern.
     
    stored variable G_DESSERTS
     
    function DessertIsOnMenu(DESSERT_NAME)
       return Exist(G_DESSERTS[DESSERT_NAME])
     
    subpattern Dessert
      if DessertIsOnMenu(VALUE)
        return 100
      return 0
     
    The Dessert subpattern above is a BuddyScript subpattern -- a subpattern that returns its match score based on some logic defined within it.  VALUE is a special variable made available for this kind of subpattern and contains the token being evaluated -- here it is passed to the DessertIsOnMenu function, which returns true if the parameter it receives exists as a key in the G_DESSERTS object. If this is the case, the token will match to Dessert with a score of 100.
     
    Using BuddyScript subpatterns enables scenarios in which you can match in a dynamic way, tightly controlling how matches are evaluated and scored.